mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-15 04:43:36 +00:00
v2.3.0
This commit is contained in:
+64
-48
@@ -1,73 +1,83 @@
|
||||
import sharp from 'sharp';
|
||||
import { readFileSync, writeFileSync, mkdirSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import sharp from "sharp";
|
||||
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = join(__dirname, '..');
|
||||
const publicDir = join(root, 'public');
|
||||
const iconsDir = join(publicDir, 'icons');
|
||||
const root = join(__dirname, "..");
|
||||
const publicDir = join(root, "public");
|
||||
const iconsDir = join(publicDir, "icons");
|
||||
|
||||
mkdirSync(iconsDir, { recursive: true });
|
||||
|
||||
const svgBuffer = readFileSync(join(publicDir, 'icon.svg'));
|
||||
const svgBuffer = readFileSync(join(publicDir, "icon.svg"));
|
||||
|
||||
const pngSizes = [16, 24, 32, 48, 64, 128, 256, 512, 1024];
|
||||
|
||||
console.log('Generating PNG icons...');
|
||||
console.log("Generating PNG icons...");
|
||||
await Promise.all(
|
||||
pngSizes.map(size =>
|
||||
pngSizes.map((size) =>
|
||||
sharp(svgBuffer)
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toFile(join(iconsDir, `${size}x${size}.png`))
|
||||
.then(() => console.log(` ✓ icons/${size}x${size}.png`))
|
||||
)
|
||||
.then(() => console.log(` ✓ icons/${size}x${size}.png`)),
|
||||
),
|
||||
);
|
||||
|
||||
// icon.png (1024x1024) for Linux electron-builder
|
||||
await sharp(svgBuffer).resize(1024, 1024).png().toFile(join(publicDir, 'icon.png'));
|
||||
console.log(' ✓ icon.png');
|
||||
await sharp(svgBuffer)
|
||||
.resize(1024, 1024)
|
||||
.png()
|
||||
.toFile(join(publicDir, "icon.png"));
|
||||
console.log(" ✓ icon.png");
|
||||
|
||||
// icon-mac.png (512x512) for macOS
|
||||
await sharp(svgBuffer).resize(512, 512).png().toFile(join(publicDir, 'icon-mac.png'));
|
||||
console.log(' ✓ icon-mac.png');
|
||||
await sharp(svgBuffer)
|
||||
.resize(512, 512)
|
||||
.png()
|
||||
.toFile(join(publicDir, "icon-mac.png"));
|
||||
console.log(" ✓ icon-mac.png");
|
||||
|
||||
// full-icon.png (1024x1024) used in app UI
|
||||
await sharp(svgBuffer).resize(1024, 1024).png().toFile(join(publicDir, 'full-icon.png'));
|
||||
console.log(' ✓ full-icon.png');
|
||||
await sharp(svgBuffer)
|
||||
.resize(1024, 1024)
|
||||
.png()
|
||||
.toFile(join(publicDir, "full-icon.png"));
|
||||
console.log(" ✓ full-icon.png");
|
||||
|
||||
// favicon.ico — embed 16, 32, 48 px layers
|
||||
console.log('Generating favicon.ico...');
|
||||
console.log("Generating favicon.ico...");
|
||||
const icoSizes = [16, 32, 48];
|
||||
const icoBuffers = await Promise.all(
|
||||
icoSizes.map(size =>
|
||||
sharp(svgBuffer).resize(size, size).png().toBuffer()
|
||||
)
|
||||
icoSizes.map((size) => sharp(svgBuffer).resize(size, size).png().toBuffer()),
|
||||
);
|
||||
writeFileSync(join(publicDir, 'favicon.ico'), buildIco(icoBuffers, icoSizes));
|
||||
console.log(' ✓ favicon.ico');
|
||||
writeFileSync(join(publicDir, "favicon.ico"), buildIco(icoBuffers, icoSizes));
|
||||
console.log(" ✓ favicon.ico");
|
||||
|
||||
// icon.ico — embed 16, 32, 48, 64, 128, 256 px layers
|
||||
console.log('Generating icon.ico...');
|
||||
console.log("Generating icon.ico...");
|
||||
const winSizes = [16, 32, 48, 64, 128, 256];
|
||||
const winBuffers = await Promise.all(
|
||||
winSizes.map(size =>
|
||||
sharp(svgBuffer).resize(size, size).png().toBuffer()
|
||||
)
|
||||
winSizes.map((size) => sharp(svgBuffer).resize(size, size).png().toBuffer()),
|
||||
);
|
||||
writeFileSync(join(publicDir, 'icon.ico'), buildIco(winBuffers, winSizes));
|
||||
console.log(' ✓ icon.ico');
|
||||
writeFileSync(join(publicDir, "icon.ico"), buildIco(winBuffers, winSizes));
|
||||
console.log(" ✓ icon.ico");
|
||||
|
||||
// icons/icon.ico and icons/icon.icns placeholders (stubs pointing to source)
|
||||
// electron-builder generates .icns; copy the 1024 PNG as icons/icon.png for reference
|
||||
await sharp(svgBuffer).resize(1024, 1024).png().toFile(join(iconsDir, 'icon.ico').replace('icon.ico', '1024x1024.png'));
|
||||
await sharp(svgBuffer)
|
||||
.resize(1024, 1024)
|
||||
.png()
|
||||
.toFile(join(iconsDir, "icon.ico").replace("icon.ico", "1024x1024.png"));
|
||||
// Copy icon.ico and icon.icns into icons/ as well
|
||||
import { copyFileSync } from 'fs';
|
||||
copyFileSync(join(publicDir, 'icon.ico'), join(iconsDir, 'icon.ico'));
|
||||
console.log(' ✓ icons/icon.ico');
|
||||
import { copyFileSync } from "fs";
|
||||
copyFileSync(join(publicDir, "icon.ico"), join(iconsDir, "icon.ico"));
|
||||
console.log(" ✓ icons/icon.ico");
|
||||
|
||||
console.log('\nDone! Note: icon.icns requires macOS tools (iconutil). Use electron-builder on macOS to generate it.');
|
||||
console.log(
|
||||
"\nDone! Note: icon.icns requires macOS tools (iconutil). Use electron-builder on macOS to generate it.",
|
||||
);
|
||||
|
||||
/**
|
||||
* Builds a minimal ICO file from PNG buffers.
|
||||
@@ -84,7 +94,13 @@ function buildIco(pngBuffers, sizes) {
|
||||
let offset = dirSize;
|
||||
const entries = pngBuffers.map((buf, i) => {
|
||||
const size = sizes[i];
|
||||
const entry = { size, width: size > 255 ? 0 : size, height: size > 255 ? 0 : size, buf, offset };
|
||||
const entry = {
|
||||
size,
|
||||
width: size > 255 ? 0 : size,
|
||||
height: size > 255 ? 0 : size,
|
||||
buf,
|
||||
offset,
|
||||
};
|
||||
offset += buf.length;
|
||||
return entry;
|
||||
});
|
||||
@@ -93,25 +109,25 @@ function buildIco(pngBuffers, sizes) {
|
||||
const ico = Buffer.alloc(totalSize);
|
||||
|
||||
// ICO header
|
||||
ico.writeUInt16LE(0, 0); // reserved
|
||||
ico.writeUInt16LE(1, 2); // type: 1 = ICO
|
||||
ico.writeUInt16LE(count, 4); // image count
|
||||
ico.writeUInt16LE(0, 0); // reserved
|
||||
ico.writeUInt16LE(1, 2); // type: 1 = ICO
|
||||
ico.writeUInt16LE(count, 4); // image count
|
||||
|
||||
// Directory entries
|
||||
entries.forEach((e, i) => {
|
||||
const base = headerSize + i * dirEntrySize;
|
||||
ico.writeUInt8(e.width, base); // width (0 = 256)
|
||||
ico.writeUInt8(e.height, base + 1); // height (0 = 256)
|
||||
ico.writeUInt8(0, base + 2); // color count
|
||||
ico.writeUInt8(0, base + 3); // reserved
|
||||
ico.writeUInt16LE(1, base + 4); // planes
|
||||
ico.writeUInt16LE(32, base + 6); // bit count
|
||||
ico.writeUInt32LE(e.buf.length, base + 8); // size of image data
|
||||
ico.writeUInt32LE(e.offset, base + 12); // offset of image data
|
||||
ico.writeUInt8(e.width, base); // width (0 = 256)
|
||||
ico.writeUInt8(e.height, base + 1); // height (0 = 256)
|
||||
ico.writeUInt8(0, base + 2); // color count
|
||||
ico.writeUInt8(0, base + 3); // reserved
|
||||
ico.writeUInt16LE(1, base + 4); // planes
|
||||
ico.writeUInt16LE(32, base + 6); // bit count
|
||||
ico.writeUInt32LE(e.buf.length, base + 8); // size of image data
|
||||
ico.writeUInt32LE(e.offset, base + 12); // offset of image data
|
||||
});
|
||||
|
||||
// Image data
|
||||
entries.forEach(e => e.buf.copy(ico, e.offset));
|
||||
entries.forEach((e) => e.buf.copy(ico, e.offset));
|
||||
|
||||
return ico;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const betterSqlite3Dir = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"node_modules",
|
||||
"better-sqlite3",
|
||||
);
|
||||
const macrosPath = path.join(betterSqlite3Dir, "src", "util", "macros.cpp");
|
||||
const helpersPath = path.join(betterSqlite3Dir, "src", "util", "helpers.cpp");
|
||||
const entryPath = path.join(betterSqlite3Dir, "src", "better_sqlite3.cpp");
|
||||
|
||||
function patchFile(filePath, replacements) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let source = fs.readFileSync(filePath, "utf8");
|
||||
let changed = false;
|
||||
|
||||
for (const { original, patched } of replacements) {
|
||||
if (source.includes(patched)) {
|
||||
continue;
|
||||
}
|
||||
if (!source.includes(original)) {
|
||||
continue;
|
||||
}
|
||||
source = source.replace(original, patched);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
fs.writeFileSync(filePath, source);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(betterSqlite3Dir)) {
|
||||
console.log("[patch-better-sqlite3] better-sqlite3 not found, skipping");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const macrosPatched = patchFile(macrosPath, [
|
||||
{
|
||||
original: `#define OnlyContext isolate->GetCurrentContext()
|
||||
#define OnlyAddon static_cast<Addon*>(info.Data().As<v8::External>()->Value())
|
||||
#define UseIsolate v8::Isolate* isolate = OnlyIsolate`,
|
||||
patched: `#define OnlyContext isolate->GetCurrentContext()
|
||||
#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >= 14
|
||||
#define BETTER_SQLITE3_EXTERNAL_POINTER_TAG static_cast<v8::ExternalPointerTypeTag>(0)
|
||||
#define EXTERNAL_VALUE(external) ((external)->Value(BETTER_SQLITE3_EXTERNAL_POINTER_TAG))
|
||||
#define EXTERNAL_NEW(isolate, value) v8::External::New((isolate), (value), BETTER_SQLITE3_EXTERNAL_POINTER_TAG)
|
||||
#else
|
||||
#define EXTERNAL_VALUE(external) ((external)->Value())
|
||||
#define EXTERNAL_NEW(isolate, value) v8::External::New((isolate), (value))
|
||||
#endif
|
||||
#define OnlyAddon static_cast<Addon*>(EXTERNAL_VALUE(info.Data().As<v8::External>()))
|
||||
#define UseIsolate v8::Isolate* isolate = OnlyIsolate`,
|
||||
},
|
||||
]);
|
||||
|
||||
const helpersPatched = patchFile(helpersPath, [
|
||||
{
|
||||
original: `\trecv->InstanceTemplate()->SetNativeDataProperty(
|
||||
\t\tInternalizedFromLatin1(isolate, name),
|
||||
\t\tfunc,
|
||||
\t\t0,
|
||||
\t\tdata
|
||||
\t);`,
|
||||
patched: `\trecv->InstanceTemplate()->SetNativeDataProperty(
|
||||
\t\tInternalizedFromLatin1(isolate, name),
|
||||
\t\tfunc,
|
||||
\t\tstatic_cast<v8::AccessorNameSetterCallback>(nullptr),
|
||||
\t\tdata
|
||||
\t);`,
|
||||
},
|
||||
]);
|
||||
|
||||
const entryPatched = patchFile(entryPath, [
|
||||
{
|
||||
original: `#include <sqlite3.h>
|
||||
#include <node.h>`,
|
||||
patched: `#include <sqlite3.h>
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address)
|
||||
#include <intrin.h>
|
||||
#define __builtin_frame_address(level) _AddressOfReturnAddress()
|
||||
#endif
|
||||
#include <node.h>`,
|
||||
},
|
||||
{
|
||||
original: `\tv8::Local<v8::External> data = v8::External::New(isolate, addon);`,
|
||||
patched: `\tv8::Local<v8::External> data = EXTERNAL_NEW(isolate, addon);`,
|
||||
},
|
||||
]);
|
||||
|
||||
if (macrosPatched || helpersPatched || entryPatched) {
|
||||
console.log(
|
||||
"[patch-better-sqlite3] Applied compatibility patches for newer Electron/V8",
|
||||
);
|
||||
} else {
|
||||
console.log(
|
||||
"[patch-better-sqlite3] Already patched or target code not found",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"node_modules",
|
||||
"guacamole-lite",
|
||||
"lib",
|
||||
"GuacdClient.js",
|
||||
);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.log("[patch-guacamole-lite] File not found, skipping");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
let content = fs.readFileSync(filePath, "utf8");
|
||||
|
||||
const oldCheck = "if (version === '1_0_0' || version === '1_1_0') {";
|
||||
const newCheck =
|
||||
"if (version === '1_0_0' || version === '1_1_0' || version === '1_3_0' || version === '1_5_0') {";
|
||||
|
||||
if (content.includes(newCheck)) {
|
||||
console.log("[patch-guacamole-lite] Already patched");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!content.includes(oldCheck)) {
|
||||
console.log("[patch-guacamole-lite] Target code not found, skipping");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
content = content.replace(oldCheck, newCheck);
|
||||
fs.writeFileSync(filePath, content);
|
||||
console.log(
|
||||
"[patch-guacamole-lite] Patched to support protocol VERSION_1_3_0 and VERSION_1_5_0",
|
||||
);
|
||||
@@ -0,0 +1,121 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const nanDir = path.join(__dirname, "..", "node_modules", "nan");
|
||||
|
||||
if (!fs.existsSync(nanDir)) {
|
||||
console.log("[patch-nan] nan not found, skipping");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function patchFile(filePath, replacements) {
|
||||
if (!fs.existsSync(filePath)) return false;
|
||||
|
||||
let source = fs.readFileSync(filePath, "utf8");
|
||||
let changed = false;
|
||||
|
||||
for (const { original, patched } of replacements) {
|
||||
if (source.includes(patched)) continue;
|
||||
if (!source.includes(original)) continue;
|
||||
source = source.replace(original, patched);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) fs.writeFileSync(filePath, source);
|
||||
return changed;
|
||||
}
|
||||
|
||||
// 1. nan.h: inject MSVC __builtin_frame_address compat before node.h is included.
|
||||
const nanHeaderPatched = patchFile(path.join(nanDir, "nan.h"), [
|
||||
{
|
||||
original: `#include <node_version.h>
|
||||
|
||||
#define NODE_0_10_MODULE_VERSION 11`,
|
||||
patched: `#include <node_version.h>
|
||||
|
||||
// MSVC lacks __builtin_frame_address; cppgc/heap.h (pulled by node.h) uses it.
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address)
|
||||
# include <intrin.h>
|
||||
# define __builtin_frame_address(level) _AddressOfReturnAddress()
|
||||
#endif
|
||||
|
||||
#define NODE_0_10_MODULE_VERSION 11`,
|
||||
},
|
||||
]);
|
||||
|
||||
// cpu-features binding.cc includes <node.h> before <nan.h>, so the nan.h patch
|
||||
// above is too late. Patch binding.cc directly to inject the compat define first.
|
||||
const cpuFeaturesDir = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"node_modules",
|
||||
"cpu-features",
|
||||
);
|
||||
const bindingPath = path.join(cpuFeaturesDir, "src", "binding.cc");
|
||||
const bindingPatched = patchFile(bindingPath, [
|
||||
{
|
||||
original: `#include <node.h>`,
|
||||
patched: `#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address)
|
||||
#include <intrin.h>
|
||||
#define __builtin_frame_address(level) _AddressOfReturnAddress()
|
||||
#endif
|
||||
#include <node.h>`,
|
||||
},
|
||||
]);
|
||||
|
||||
// 2. nan_implementation_12_inl.h: replace v8::External::New() with the 3-arg form.
|
||||
// Electron 42 / V8 13+ requires an ExternalPointerTypeTag as the third argument.
|
||||
const implPath = path.join(nanDir, "nan_implementation_12_inl.h");
|
||||
let implPatched = false;
|
||||
if (fs.existsSync(implPath)) {
|
||||
let src = fs.readFileSync(implPath, "utf8");
|
||||
const before = src;
|
||||
|
||||
const TAG = "static_cast<v8::ExternalPointerTypeTag>(0)";
|
||||
if (!src.includes(TAG)) {
|
||||
src = src.replace(
|
||||
/v8::External::New\(v8::Isolate::GetCurrent\(\),\s*value\)/g,
|
||||
`v8::External::New(v8::Isolate::GetCurrent(), value, ${TAG})`,
|
||||
);
|
||||
src = src.replace(
|
||||
/v8::External::New\(isolate,\s*reinterpret_cast<void \*>\(callback\)\)/g,
|
||||
`v8::External::New(isolate, reinterpret_cast<void *>(callback), ${TAG})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (src !== before) {
|
||||
fs.writeFileSync(implPath, src);
|
||||
implPatched = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. nan_callbacks_12_inl.h: replace ->Value() with ->Value(tag) on v8::External.
|
||||
// The new API requires an ExternalPointerTypeTag argument.
|
||||
const callbacksPath = path.join(nanDir, "nan_callbacks_12_inl.h");
|
||||
let callbacksPatched = false;
|
||||
if (fs.existsSync(callbacksPath)) {
|
||||
let src = fs.readFileSync(callbacksPath, "utf8");
|
||||
const before = src;
|
||||
|
||||
const TAG = "static_cast<v8::ExternalPointerTypeTag>(0)";
|
||||
if (!src.includes(TAG)) {
|
||||
// Pattern: .As<v8::External>()->Value()) — always followed by ))
|
||||
src = src.replace(
|
||||
/\.As<v8::External>\(\)->Value\(\)\)/g,
|
||||
`.As<v8::External>()->Value(${TAG}))`,
|
||||
);
|
||||
}
|
||||
|
||||
if (src !== before) {
|
||||
fs.writeFileSync(callbacksPath, src);
|
||||
callbacksPatched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nanHeaderPatched || bindingPatched || implPatched || callbacksPatched) {
|
||||
console.log(
|
||||
"[patch-nan] Applied compatibility patches for Electron 42 / V8 13+",
|
||||
);
|
||||
} else {
|
||||
console.log("[patch-nan] Already patched or target code not found");
|
||||
}
|
||||
Reference in New Issue
Block a user