first commit

This commit is contained in:
Fabio 2026-01-07 15:52:21 +01:00
commit 34cf681f71
11 changed files with 6859 additions and 0 deletions

40
.gitignore vendored Normal file
View file

@ -0,0 +1,40 @@
# --- Node ---
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# --- Capacitor ---
android/
ios/
capacitor.config.json.backup
app/public/build/
app/public/dist/
app/public/.vite/
# --- Electron ---
dist/
electron/
dist-electron/
out/
release/
build/
*.asar
# Electron Builder temp
*.dmg
*.zip
*.pkg
# --- System files ---
.DS_Store
Thumbs.db
# --- Logs ---
logs/
*.log
# --- Environment ---
.env
.env.*

218
README.md Normal file
View file

@ -0,0 +1,218 @@
# MacOS con Capacitor Electon
creare un foder
inizializza Capacitor
```
npm install @capacitor/core @capacitor/cli
npx cap init
npx cap init "MyApps" "it.patachina.myapps" --web-dir=web
```
inserisci in name il nome che vuoi x es MyApps
in id xesemoio it.patachina.myapps
in Web asset directory metti web (la dir dove si trova il file statico)
aggiungi Electon
```
npm install @capacitor-community/electron electron
npx cap add @capacitor-community/electron
```
copia il file statico nella cartella web
per esempio
```
/web/index.html
/web/style.css
/web/app.js
```
build e sync
```
npx cap sync
```
avvia l'app macOS per testare se funziona
```
npx cap open @capacitor-community/electron
```
crea il pacchetto .app
```
npm install electron-builder --save-dev
```
modifica il tuo package.json inserendo alla fine
```
"scripts": {
"electron:build": "electron-builder -c electron-builder.json"
}
```
oppure per una versione più semplice
```
"scripts": {
"electron:build": "electron-builder"
}
```
inserisci anche i campi name,version,description,author,main
inoltre
```
"electron": "^39.2.7"
```
deve essere in devDependencies non dependencies
il file deve diventare
```
{
"name": "MyApps",
"version": "1.0.0",
"description": "Cross-platform launcher built with Capacitor and Electron",
"author": "Fabio",
"main": "electron/main.js",
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor/cli": "^7.4.4",
"@capacitor/core": "^8.0.0"
},
"devDependencies": {
"electron": "^39.2.7",
"electron-builder": "^26.0.12"
},
"scripts": {
"electron:build": "electron-builder -c electron-builder.json"
}
}
```
nella root aggiungi il file electron-builder.json
```
{
"appId": "it.fabio.myapps",
"productName": "MyApps",
"directories": {
"output": "dist-electron"
},
"files": [
"electron/**/*",
"web/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"icon": "electron/assets/casina.icns",
"identity": null,
"hardenedRuntime": false,
"gatekeeperAssess": false,
"notarize": false
},
"win": {
"target": "nsis"
},
"linux": {
"target": "AppImage"
}
}
```
visti i tempi lunghi di certificazione solo quando va bene puoi modificare in
```
"identity": "MyCustomCert",
```
vedi sotto come creare la MyCustomCert
copiare casina.icns (macos vuole solo quella estensione)
```
cp icons/casina.icns electron/assets/
```
in electron/assets/
creare in electron il file main.js
```
nano electron/main.js
```
e inserire
```
// electron/main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
});
win.loadFile(path.join(__dirname, '../web/index.html'));
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
```
ora puoi builare
```
npm run electron:build
```
## Genera chiave privata + certificato con estensioni Code Signing
```
openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout mycert.key \
-out mycert.crt \
-config codesign.cnf
```
Unisci chiave + certificato in un PEM
```
cat mycert.key mycert.crt > mycert.pem
```
Importa nel portachiavi login
````
security import mycert.pem -k ~/Library/Keychains/login.keychain-db -A
```
rendere il certificato attendibile per la firma codice
1⃣ Apri Accesso Portachiavi
Applicazioni → Utility → Accesso Portachiavi
2⃣ Colonna sinistra → seleziona login
3⃣ Colonna centrale → vai su I miei certificati
4⃣ Fai doppio clic su MyCustomCert
5⃣ Vai nella sezione Attendibilità
6⃣ Imposta:
👉 Quando si usa questo certificato: Considera sempre attendibile
7⃣ Chiudi la finestra
macOS ti chiede la password → conferma.

161
README2.md Normal file
View file

@ -0,0 +1,161 @@
# MacOS con Capacitor Electon (non funziona)
creare un foder per esempio launch ed entraci
```
mkdir launch
cd launch
```
crea una cartella per il file statico, in questo caso web
```
mkdir web
```
copia il file statico nella cartella web
per esempio
```
/web/index.html
/web/style.css
/web/app.js
```
inizializza capacitor
```
npm init -y
npm install @capacitor/core @capacitor/cli
npx cap init "MyApps" "it.patachina.myapps" --web-dir=web
```
qui ho chiamato la mia app "MyApps", l'ID della app "it.patachina.myapps" e la webdir quella creata prima "web"
ora aggiungo Electron come piattaforma a Capacitor
```
npm install @capacitor-community/electron
npx cap add @capacitor-community/electron
```
inserisci in capacitor.config.json
```
"bundledWebRuntime": false
```
copia i file statici dentro Electron
```
npx cap copy
```
questo copia i file statici in electron/app
entra in electron
```
cd electron
```
e se il progetto non è ts ma solo js modifica tsconfig.json
```
nano tsconfig.json
```
inserendo in compileOptions types e skipLibCheck
```
{
"compilerOptions": {
"types": ["node"],
"skipLibCheck": true
}
}
```
per creare .dmg file bisogna installare electron build (sempre nella directory electron)
```
npm install -D electron-builder
```
in package.json sempre dentro electron aggiungi negli scripts la sezione "electron:build"
```
"scripts": {
"electron:build": "electron-builder"
}
```
e aggiungi (o verifica) la sezione build (dove ho messo name, ID, risoluzione)
inoltre l'icona casina.icns che era nel file statico in web, se non ho l'icona rimuovo "icon" userà quella di default
inserito anche identity, per crearla vedi sotto
```
{
"name": "MyApps",
"version": "1.0.0",
"build": {
"appId": "it.patachina.myapps",
"files": [
"build/**/*",
"package.json"
],
"mac": {
"target": ["dmg", "zip"],
"category": "public.app-category.productivity",
"icon": "app/casina.icns",
"identity": "MyCustomCert",
"hardenedRuntime": false,
"gatekeeperAssess": false
},
"dmg": {
"background": null,
"window": {
"width": 540,
"height": 380
}
}
},
"description": "An Amazing Capacitor App",
```
costruisci il build e fallo partire
```
npm run build
npm run electron:start
```
per creare .dmg file, sempre in electron, eseguire il Comando per generare il DMG
```
npm run electron:build
```
## Crea autocertificato
Crea un file di configurazione OpenSSL
Crea codesign.cnf:
```
cat <<EOF > codesign.cnf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
x509_extensions = ext
[ dn ]
CN = MyCustomCert
[ ext ]
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, codeSigning
basicConstraints = critical, CA:FALSE
EOF
```

5
capacitor.config.json Normal file
View file

@ -0,0 +1,5 @@
{
"appId": "it.patachina.myapps",
"appName": "MyApps",
"webDir": "web"
}

25
electron-builder.json Normal file
View file

@ -0,0 +1,25 @@
{
"appId": "it.fabio.myapps",
"productName": "MyApps",
"directories": {
"output": "dist-electron"
},
"files": [
"electron/**/*",
"web/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"icon": "electron/assets/casina.icns",
"identity": null,
"hardenedRuntime": false,
"gatekeeperAssess": false,
"notarize": false
},
"win": {
"target": "nsis"
},
"linux": {
"target": "AppImage"
}
}

BIN
icons/casina.icns Normal file

Binary file not shown.

5078
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

19
package.json Normal file
View file

@ -0,0 +1,19 @@
{
"name": "MyApps",
"version": "1.0.0",
"description": "Cross-platform launcher built with Capacitor and Electron",
"author": "Fabio",
"main": "electron/main.js",
"dependencies": {
"@capacitor-community/electron": "^5.0.1",
"@capacitor/cli": "^7.4.4",
"@capacitor/core": "^8.0.0"
},
"devDependencies": {
"electron-builder": "^26.4.0",
"electron": "^39.2.7"
},
"scripts": {
"electron:build": "electron-builder"
}
}

1006
web/app.js Normal file

File diff suppressed because it is too large Load diff

48
web/index.html Normal file
View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>MyApps Launcher</title>
<!-- Blocca lo zoom del browser -->
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="setup-page" class="hidden">
<h2>Configurazione</h2>
<button id="cfg-refresh">Aggiorna ora</button>
<label>URL</label>
<input id="cfg-url" type="text">
<label>User</label>
<input id="cfg-user" type="text">
<label>Password</label>
<input id="cfg-pass" type="password">
<button id="cfg-save">Salva</button>
</div>
<!-- Griglia icone -->
<div class="folder" id="folder"></div>
<!-- Menu contestuale -->
<div id="context-menu" class="context-menu hidden">
<button data-action="rename">Rinomina</button>
<button data-action="change-icon">Cambia icona</button>
<button data-action="remove">Rimuovi</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<script src="app.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>
eruda.init();
</script> -->
</body>
</html>

259
web/style.css Normal file
View file

@ -0,0 +1,259 @@
/* ============================================================
BASE PAGE
============================================================ */
html, body {
margin: 0;
padding: 0;
overflow-x: hidden; /* impedisce pan orizzontale */
max-width: 100%;
touch-action: pan-y; /* solo scroll verticale */
background: #ffffff;
/*background: radial-gradient(circle at top, #f8f9ff 0%, #e6e8ef 60%, #dcdfe6 100%);*/
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
color: #1a1a1a;
min-height: 100vh; /* evita scroll inutile se poche icone */
}
/* Impedisce selezione testo e highlight blu Android */
* {
-webkit-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
/* Variabile di zoom globale */
:root {
--zoom: 1;
}
/* ============================================================
GRIGLIA ICONE
============================================================ */
.folder {
display: grid;
grid-template-columns: repeat(
auto-fill,
minmax(calc(85px * var(--zoom)), 1fr)
);
gap: calc(16px * var(--zoom));
padding-top: 24px;
padding-left: 24px;
padding-right: 24px;
padding-bottom: 0;
justify-items: start; /* più coerente con iOS */
width: 100%;
max-width: 100%;
box-sizing: border-box;
transition: grid-template-columns 0.15s ease-out,
gap 0.15s ease-out;
}
/* Contenitore icona — versione glass */
.app-icon {
text-align: center;
cursor: pointer;
user-select: none;
touch-action: none;
transition: transform 0.18s ease, filter 0.18s ease;
/* GLASS */
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: calc(20px * var(--zoom));
padding: calc(6px * var(--zoom));
box-sizing: border-box;
overflow: hidden;
}
/* Icona PNG */
.app-icon img {
width: calc(78px * var(--zoom));
height: calc(78px * var(--zoom));
border-radius: calc(16px * var(--zoom));
background: transparent;
pointer-events: none;
box-shadow:
0 4px 10px rgba(0, 0, 0, 0.12),
0 8px 24px rgba(0, 0, 0, 0.08);
display: block;
}
/* Etichetta */
.app-icon span {
display: block;
margin-top: calc(6px * var(--zoom));
font-size: calc(11px * var(--zoom));
color: #3a3a3a;
font-weight: 500;
letter-spacing: -0.2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: font-size 0.18s ease-out,
margin-top 0.18s ease-out;
}
/* ============================================================
WIGGLE MODE
============================================================ */
@keyframes wiggle {
0% { transform: rotate(-2deg) scale(1.02); }
50% { transform: rotate( 2deg) scale(0.98); }
100% { transform: rotate(-2deg) scale(1.02); }
}
.edit-mode .app-icon:not(.dragging) img {
animation: wiggle 0.25s ease-in-out infinite;
}
/* Icona trascinata */
.app-icon.dragging {
opacity: 0.9;
z-index: 1000;
}
/* Placeholder invisibile */
.app-icon.placeholder {
opacity: 0;
visibility: hidden;
}
/* ============================================================
MENU CONTESTUALE ANDROID MATERIAL + RESPONSIVE ALLO ZOOM
============================================================ */
#context-menu {
position: fixed;
background: #ffffff;
border-radius: calc(14px * var(--zoom));
min-width: calc(180px * var(--zoom));
padding: calc(8px * var(--zoom)) 0;
z-index: 2000;
/* Ombra Material */
box-shadow:
0 calc(6px * var(--zoom)) calc(20px * var(--zoom)) rgba(0,0,0,0.18),
0 calc(2px * var(--zoom)) calc(6px * var(--zoom)) rgba(0,0,0,0.12);
/* Animazione apertura */
opacity: 0;
transform: scale(0.85);
transform-origin: top center;
transition: opacity 120ms ease, transform 120ms ease;
}
#context-menu:not(.hidden) {
opacity: 1;
transform: scale(1);
}
#context-menu.hidden {
display: block;
opacity: 0;
pointer-events: none;
}
/* Pulsanti del menù */
#context-menu button {
all: unset;
width: 100%;
padding: calc(14px * var(--zoom)) calc(18px * var(--zoom));
font-size: calc(15px * var(--zoom));
color: #222;
display: flex;
align-items: center;
gap: calc(12px * var(--zoom));
cursor: pointer;
position: relative;
overflow: hidden;
}
/* Ripple effect */
#context-menu button::after {
content: "";
position: absolute;
inset: 0;
background: rgba(0,0,0,0.08);
opacity: 0;
transition: opacity 150ms;
}
#context-menu button:active::after {
opacity: 1;
}
/* Separatore tra voci */
#context-menu button + button {
border-top: 1px solid rgba(0,0,0,0.08);
}
/* Voce "Rimuovi" in rosso */
#context-menu button:last-child {
color: #d11a2a;
}
/* Permette drag sia mouse che touch */
.app-icon {
touch-action: none;
}
/* Evita che l'immagine intercetti eventi */
.app-icon img {
pointer-events: none;
}
/* Allineamento stile iOS, evita offset su PC */
.folder {
justify-items: start;
}
/* ============================================================
PAGINA INIZIALE
============================================================ */
#setup-page {
position: fixed;
inset: 0;
background: #f5f5f7;
padding: 40px;
display: flex;
flex-direction: column;
gap: 16px;
z-index: 9999;
}
#setup-page.hidden {
display: none;
}
#setup-page input {
padding: 12px;
font-size: 16px;
border-radius: 8px;
border: 1px solid #ccc;
}
#setup-page button {
padding: 14px;
font-size: 16px;
border-radius: 8px;
background: #007aff;
color: white;
border: none;
}
#cfg-refresh {
display: none;
}