create-athenea-app 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/index.js CHANGED
@@ -266,6 +266,14 @@ async function main() {
266
266
  await fsp.mkdir(targetDir, { recursive: true });
267
267
  await fsp.cp(templateDir, targetDir, { recursive: true });
268
268
 
269
+ // npm elimina archivos ".gitignore" al publicar el paquete, por eso el
270
+ // template lo distribuye como "gitignore" (sin punto) y lo renombramos aca.
271
+ const gitignoreSrc = path.join(targetDir, "gitignore");
272
+ const gitignoreDest = path.join(targetDir, ".gitignore");
273
+ if (fs.existsSync(gitignoreSrc)) {
274
+ await fsp.rename(gitignoreSrc, gitignoreDest);
275
+ }
276
+
269
277
  await updateAppPackageJson(targetDir, { npmName, productName, appId });
270
278
 
271
279
  const tokenMap = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-athenea-app",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "CLI para scaffoldear apps de escritorio con Electron + Preact + Vite (DX rapida: dev server, hot reload, build y empaquetado)",
6
6
  "keywords": [
@@ -0,0 +1,23 @@
1
+ # AGENTS - create-athenea-app/template/
2
+
3
+ ## Alcance
4
+
5
+ - Aplica solo a `create-athenea-app/template/**`.
6
+
7
+ ## Reglas especificas
8
+
9
+ - Esta carpeta es un proyecto generado para usuarios finales: mantenerla autocontenida y ejecutable tras scaffolding.
10
+ - Si se cambian rutas, nombres de archivos o placeholders, actualizar `create-athenea-app/bin/index.js` en la misma tarea.
11
+ - Mantener paridad funcional minima entre template y app de referencia (entrypoints electron-vite y estructura `src/main`, `src/preload`, `src/renderer`).
12
+ - No agregar archivos generados (`out/`, `release/`, `node_modules/`) dentro del template.
13
+
14
+ ## Auto-invocacion de skills
15
+
16
+ - Invocar `skills/create-athenea-template-sync/SKILL.md` al tocar cualquier archivo del template.
17
+ - Si se modifica IPC en template, usar tambien `skills/electron-ipc-contract/SKILL.md`.
18
+
19
+ ## Checklist obligatorio
20
+
21
+ - [ ] Verificar que el template siga resolviendo `src/main/index.js`, `src/preload/index.js` y `src/renderer/index.html`.
22
+ - [ ] Verificar que los placeholders esperados por el CLI sigan presentes donde corresponda.
23
+ - [ ] Validar scaffolding con `node create-athenea-app/bin/index.js demo --no-install` en carpeta temporal.
@@ -1,4 +1,4 @@
1
- # **APP_TITLE**
1
+ # __APP_TITLE__
2
2
 
3
3
  Aplicación de escritorio creada con [Athenea](https://github.com/ignadev/Athenea-Desktop).
4
4
 
@@ -0,0 +1,42 @@
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ # npm and package manager caches
11
+ .npm/
12
+ .pnpm-store/
13
+
14
+ # Build artifacts
15
+ out/
16
+ release/
17
+ dist/
18
+ dist-ssr/
19
+
20
+ # Tooling caches
21
+ .vite/
22
+ .cache/
23
+ .eslintcache
24
+ coverage/
25
+ *.tsbuildinfo
26
+
27
+ node_modules/
28
+ *.local
29
+
30
+ # Environment variables
31
+ .env
32
+
33
+ # Editor directories and files
34
+ .vscode/*
35
+ !.vscode/extensions.json
36
+ .idea
37
+ .DS_Store
38
+ *.suo
39
+ *.ntvs*
40
+ *.njsproj
41
+ *.sln
42
+ *.sw?
@@ -7,7 +7,7 @@
7
7
  "type": "module",
8
8
  "main": "./out/main/index.js",
9
9
  "engines": {
10
- "node": ">=22.0.0"
10
+ "node": ">=22.12.0"
11
11
  },
12
12
  "scripts": {
13
13
  "dev": "electron-vite dev",
@@ -25,10 +25,10 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@preact/preset-vite": "^2.10.1",
28
- "electron": "^35.1.4",
29
- "electron-builder": "^26.0.0",
28
+ "electron": "^43.1.0",
29
+ "electron-builder": "^26.15.6",
30
30
  "electron-vite": "^5.0.0",
31
- "vite": "^7.2.2"
31
+ "vite": "^7.3.6"
32
32
  },
33
33
  "build": {
34
34
  "appId": "com.example.app",
@@ -12,6 +12,31 @@ const childWindowsByRoute = new Map();
12
12
 
13
13
  const settingsPath = path.join(app.getPath("userData"), "settings.json");
14
14
 
15
+ /** Origen permitido para navegación (dev server o file:// en producción). */
16
+ function isNavigationAllowed(targetUrl) {
17
+ try {
18
+ const target = new URL(targetUrl);
19
+ if (process.env.ELECTRON_RENDERER_URL) {
20
+ const devOrigin = new URL(process.env.ELECTRON_RENDERER_URL).origin;
21
+ return target.origin === devOrigin;
22
+ }
23
+ return target.protocol === "file:";
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
28
+
29
+ /** Bloquea apertura de ventanas nuevas y navegación fuera del origen propio. */
30
+ function hardenWebContents(webContents) {
31
+ webContents.setWindowOpenHandler(() => ({ action: "deny" }));
32
+
33
+ webContents.on("will-navigate", (event, targetUrl) => {
34
+ if (!isNavigationAllowed(targetUrl)) {
35
+ event.preventDefault();
36
+ }
37
+ });
38
+ }
39
+
15
40
  function createWindow() {
16
41
  const { width: screenWidth, height: screenHeight } =
17
42
  screen.getPrimaryDisplay().workAreaSize;
@@ -22,7 +47,7 @@ function createWindow() {
22
47
  title: __APP_TITLE_JSON__,
23
48
  webPreferences: {
24
49
  preload: join(__dirname, "../preload/index.mjs"),
25
- sandbox: false,
50
+ sandbox: true,
26
51
  contextIsolation: true,
27
52
  nodeIntegration: false,
28
53
  },
@@ -32,6 +57,8 @@ function createWindow() {
32
57
  mainWindow.setAutoHideMenuBar(true);
33
58
  mainWindow.maximize();
34
59
 
60
+ hardenWebContents(mainWindow.webContents);
61
+
35
62
  // electron-vite: usa ELECTRON_RENDERER_URL en dev
36
63
  if (process.env.ELECTRON_RENDERER_URL) {
37
64
  mainWindow.loadURL(process.env.ELECTRON_RENDERER_URL);
@@ -55,6 +82,10 @@ function createWindow() {
55
82
  }
56
83
  }
57
84
  });
85
+
86
+ mainWindow.on("closed", () => {
87
+ mainWindow = null;
88
+ });
58
89
  }
59
90
 
60
91
  app.whenReady().then(() => {
@@ -121,7 +152,7 @@ ipcMain.on("window:openRoute", (_event, options) => {
121
152
  title: title || __APP_TITLE_JSON__,
122
153
  webPreferences: {
123
154
  preload: join(__dirname, "../preload/index.mjs"),
124
- sandbox: false,
155
+ sandbox: true,
125
156
  contextIsolation: true,
126
157
  nodeIntegration: false,
127
158
  },
@@ -129,6 +160,9 @@ ipcMain.on("window:openRoute", (_event, options) => {
129
160
 
130
161
  child.setMenuBarVisibility(false);
131
162
  child.setAutoHideMenuBar(true);
163
+
164
+ hardenWebContents(child.webContents);
165
+
132
166
  if (mainWindow && !mainWindow.isDestroyed()) {
133
167
  child.setParentWindow(mainWindow);
134
168
  }
@@ -2,7 +2,11 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="./public/logo.svg" />
5
+ <meta
6
+ http-equiv="Content-Security-Policy"
7
+ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' ws: wss:;"
8
+ />
9
+ <link rel="icon" type="image/png" href="/logo.png" />
6
10
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
11
  <title>__APP_TITLE__</title>
8
12
  </head>
@@ -1,13 +1,22 @@
1
- import { LocationProvider, Router, Route } from "preact-iso";
2
- import Home from "./routes/Home/Home";
1
+ import { LocationProvider, Router, Route } from 'preact-iso'
2
+ import Home from './routes/Home/Home'
3
+
4
+ /** Reads the `route` query param (used by main-process child windows) so the
5
+ * renderer navigates to the requested route on startup instead of always
6
+ * resolving to the current `location.pathname`. */
7
+ function getInitialUrl() {
8
+ const params = new URLSearchParams(location.search)
9
+ const route = params.get('route')
10
+ return route || location.pathname + location.search
11
+ }
3
12
 
4
13
  export default function App() {
5
- return (
6
- <LocationProvider>
7
- <Router>
8
- <Route path="/" component={Home} />
9
- <Route default component={Home} />
10
- </Router>
11
- </LocationProvider>
12
- );
14
+ return (
15
+ <LocationProvider url={getInitialUrl()}>
16
+ <Router>
17
+ <Route path="/" component={Home} />
18
+ <Route default component={Home} />
19
+ </Router>
20
+ </LocationProvider>
21
+ )
13
22
  }
@@ -1,14 +1,14 @@
1
- import { useState } from "preact/hooks";
2
- import "./style.css";
1
+ import { useState } from 'preact/hooks'
2
+ import './style.css'
3
3
 
4
4
  export function Counter() {
5
- const [count, setCount] = useState(0);
5
+ const [count, setCount] = useState(0)
6
6
 
7
- return (
8
- <div class="counter">
9
- <button onClick={() => setCount(count - 1)}>-</button>
10
- <p>{count}</p>
11
- <button onClick={() => setCount(count + 1)}>+</button>
12
- </div>
13
- );
7
+ return (
8
+ <div class="counter">
9
+ <button onClick={() => setCount(count - 1)}>-</button>
10
+ <p>{count}</p>
11
+ <button onClick={() => setCount(count + 1)}>+</button>
12
+ </div>
13
+ )
14
14
  }
@@ -7,7 +7,7 @@
7
7
  html,
8
8
  body {
9
9
  height: 100%;
10
- overflow: hidden;
10
+ overflow-y: auto;
11
11
  }
12
12
 
13
13
  body {
@@ -19,5 +19,5 @@ body {
19
19
  }
20
20
 
21
21
  #app {
22
- height: 100%;
22
+ min-height: 100%;
23
23
  }
@@ -1,5 +1,5 @@
1
- import { render } from "preact";
2
- import App from "./app.jsx";
3
- import "./index.css";
1
+ import { render } from 'preact'
2
+ import App from './app.jsx'
3
+ import './index.css'
4
4
 
5
- render(<App />, document.getElementById("app"));
5
+ render(<App />, document.getElementById('app'))
@@ -1,13 +1,17 @@
1
- import { Counter } from "../../components/Counter/Counter";
1
+ import { Counter } from '../../components/Counter/Counter'
2
2
 
3
- import "./style.css";
3
+ import './style.css'
4
4
 
5
5
  export default function Home() {
6
- return (
7
- <div className="home">
8
- <h1 className="home-title">¡Bienvenido a __APP_TITLE__!</h1>
9
- <img className="home-logo" src="../../public/logo.png" alt="" />
10
- <Counter />
11
- </div>
12
- );
6
+ return (
7
+ <div className="home">
8
+ <h1 className="home-title">¡Bienvenido a __APP_TITLE__!</h1>
9
+ <img
10
+ className="home-logo"
11
+ src={`${import.meta.env.BASE_URL}logo.png`}
12
+ alt=""
13
+ />
14
+ <Counter />
15
+ </div>
16
+ )
13
17
  }
@@ -5,7 +5,7 @@
5
5
  justify-content: center;
6
6
  padding: 2rem;
7
7
  background-color: #f8f9fa;
8
- height: 100vh;
8
+ min-height: 100vh;
9
9
  box-sizing: border-box;
10
10
  font-family: "Inter", system-ui, sans-serif;
11
11
  color: #333;