Stackpress Desktop: Proposal #71
cblanquera
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Proposal for adding a
stackpress-desktoppackage that lets Stackpress apps ship as Electron desktop applications while preserving Stackpress routing, backend handlers, server-rendered views, API routes, sessions, and build behavior.1. Summary
Stackpress should support a first-class desktop app target through a new package named
stackpress-desktop.The package should not turn Stackpress apps into static SPAs by default. Instead, it should wrap the existing Stackpress runtime inside Electron:
This model matches how several mature Electron desktop tools work. For example, Eclipse Theia runs frontend and backend processes locally in Electron while using HTTP/WebSocket-style communication. JupyterLab Desktop also bundles a local backend server with an Electron shell.
2. Goals
The desktop package should make it possible to:
stackpress buildflow rather than duplicating Reactus build behavior.3. Non-Goals
The first version should not try to:
stackpress/pluginpackage unless route filtering proves insufficient.Those capabilities can be explored after the core runtime model is proven.
4. Proposed Runtime Model
The first supported runtime should be local HTTP mode. The config should still
reserve a
desktop.runtimefield so a future WHATWG custom protocol runtime canbe added without changing the public config shape.
In local HTTP mode:
stackpress-desktopbootstraps the configured Stackpress app.127.0.0.1using an available port.BrowserWindowpointed athttp://127.0.0.1:<port>/.This preserves the current Stackpress runtime contract:
configlifecycleThe default config should be explicit:
4.1. Optional Custom Protocol Mode
A later version can support a custom Electron protocol such as
stackpress://app/....That mode would likely use
stackpress/whatwgand adapt Electron protocol requests into WHATWGRequestandResponseobjects.This may be useful for a more native-looking app URL model, but it should not be the first default because local HTTP mode has fewer moving parts and better compatibility with current Stackpress behavior.
The config should reserve protocol-specific options even if the first
implementation only accepts
runtime: 'http':The first implementation can validate this as unsupported and return a clear
error. That keeps the long-term design visible without committing to the
adapter work too early.
5. Build Model
desktop:buildshould compose the existing Stackpress build flow.The desktop build should:
config,listen, androutelifecycle events.buildevent registered bystackpress-view.server.routesserver.importsserver.entriesserver.viewsThe key point is that desktop build should compose this existing flow:
The desktop package should not duplicate Reactus internals. It should invoke the Stackpress build lifecycle and then add desktop-specific artifacts around the result.
6. Package Shape
Add a new workspace package:
packages/stackpress-desktop/ LICENSE README.md package.json src/ index.ts plugin.ts MenuRegistry.ts types.ts events/ build.ts dev.ts package.ts index.ts scripts/ build.ts dev.ts package.ts main.ts preload.ts index.ts tsconfig.json tsconfig.cjs.json tsconfig.esm.jsonThe package should follow existing Stackpress package conventions:
cjsandesm./plugin,./events,./scripts,./types, and.listenlifecyclesrc/types.tsSuggested root script:
{ "scripts": { "desktop": "yarn --cwd packages/stackpress-desktop" } }7. CLI Surface
The package should add desktop-specific events and corresponding scripts.
Suggested commands:
7.1.
desktop:devStarts a desktop development session.
Expected behavior:
7.2.
desktop:buildBuilds desktop-ready Stackpress output without necessarily producing a signed installer.
Expected behavior:
stackpress build7.3.
desktop:packagePackages the desktop app for the current platform.
Expected behavior:
desktop:buildif neededPackaging should be configurable so the package can support
electron-builder,electron-forge, or a simple default later.8. Config Shape
Add a
desktopconfig namespace.Example:
The app bootstrap can then include it:
8.1. Desktop Types
The package should expose
DesktopConfigandDesktopPlugintypes so appconfigs and plugins can infer the desktop shape without importing internal
implementation details.
Suggested public types:
DesktopPluginshould be the value registered asdesktopso other plugins canuse the normal Stackpress plugin lookup pattern:
8.2. Desktop Plugin Registration
stackpress-desktopshould register Electron primitives under adesktopplugin before Electron app initialization.
Suggested registration:
This keeps raw Electron access available while still giving Stackpress plugins a
stable composition point. App plugins should not need to import Electron just to
add a menu item, register an IPC handler, or inspect desktop config.
8.3. Raw Electron Config
The desktop package should expose an escape hatch for raw Electron config,
similar to how
stackpress-viewlets apps pass raw Vite config throughview.engine.vite.Top-level desktop config should cover common and required options. Raw Electron
config should live under
desktop.electronand be merged during desktop plugininitialization.
Example:
Suggested merge order:
stackpress-desktopdefaultsdesktopconfigdesktop.electronconfigTop-level config should remain the recommended path. Raw config should be the
escape hatch for Electron options that Stackpress Desktop does not model yet.
8.4. Config Contribution Lifecycle
Other plugins should be able to contribute to desktop config before the Electron
app and windows are initialized.
Suggested lifecycle:
configandlistenevents.desktopplugin.desktop:configso plugins can mutate or extend config.Suggested events:
The important boundary is that
desktop:configruns before Electron uses theconfig to create native objects.
desktop:readyruns after the app, menus, andmain window are available.
8.5. Menu Contributions
Electron menus are tree-shaped templates. Stackpress Desktop should offer a
small menu registry so packages can contribute menu items without manually
editing one shared template array.
The registry can be based on
Setor a priority-aware collection from@stackpress/lib. Its job is to collect menu items, sort them, and compilethem into
Menu.buildFromTemplate(...).Suggested contribution shape:
Plugins should also be able to contribute submenu items:
The first implementation should support common app, file, edit, view, window,
and help menus. It can still allow raw Electron
MenuItemConstructorOptionsforadvanced cases.
8.6. Updater Support
The desktop config should reserve updater settings so desktop apps can offer a
standard "new version available" flow.
Suggested config:
Expected behavior:
autoCheckis enabled.desktop:update-available.The first milestone can provision the config, menu entry, and events without
fully solving every provider. If packaging uses
electron-builder,electron-updateris likely the practical implementation path. Electron'sbuilt-in
autoUpdatershould remain available through the registered desktopplugin for apps that want direct access.
9. Route Filtering
Desktop builds need a way to restrict the routes included in a desktop target.
This should follow the same style as the
stackpress-sessionaccess config:route rules are plain objects with a
methodandroute. The desktop packageshould not add a separate
typefield for include or ignore behavior.This matters because a desktop app may not want to ship every web route. A common example is allowing public article pages while leaving admin pages out of a public desktop build.
Proposed config:
If
desktop.routesis empty or omitted, every route is allowed by default. Ifone or more routes are configured, desktop mode switches to allowlist behavior:
only matching routes are allowed, and all other routes are blocked by default.
Route filtering should apply in two places:
Build-time filtering prevents non-allowed lazy routes, entries, and views from being included in the desktop route manifest when possible.
Runtime filtering protects against routes that were registered by plugins before the desktop package had a chance to filter build output.
9.1. Matching Rules
Each route rule should use this shape:
The route matcher should support simple route patterns first:
/admin/admin/**/**Regular expressions can be added later if needed, but simple patterns are easier to document and safer for app authors.
9.2. Allowlist Behavior
The proposed decision rule:
desktop.routesis empty or omitted, every registered route is allowed.desktop.routeshas one or more entries, a route must match at least one configured rule.This makes the default permissive for simple apps while giving desktop builds a
clear allowlist when an app needs to ship only part of the web surface.
10. Plugin Exclusion
Route filtering is useful, but it happens after plugins register behavior.
For some desktop builds, it may be cleaner to avoid loading entire plugins. For example, excluding
stackpress-adminat plugin load time is cleaner than registering admin routes and blocking them afterward.Possible future config:
This would likely require a change to how the aggregate
stackpress/pluginexport loads built-in plugins. Because that affects shared package behavior, plugin exclusion should be treated as a later design step unless route filtering is not enough.11. Template Changes
Start with
templates/blogbecause it is the primary end-to-end example and exercises the important surfaces:Suggested additions:
Suggested package scripts:
{ "scripts": { "desktop:dev": "dotenv -e .env -- stackpress desktop:dev --b config/desktop -v", "desktop:build": "dotenv -e .env -- stackpress desktop:build --b config/desktop -v", "desktop:package": "dotenv -e .env -- stackpress desktop:package --b config/desktop -v" } }A separate
templates/desktopcan be added later if the desktop target needs a dedicated starter.12. Security Considerations
Desktop mode should use a conservative Electron setup by default.
Recommended defaults:
127.0.0.1Local HTTP mode means the local server may be reachable by local processes. This is acceptable for a first version if the server binds only to loopback and follows normal Stackpress auth/session rules, but it should be documented clearly.
13. Local Data
Desktop apps should be able to use local data paths that differ from development paths.
For example, a desktop app using PGlite should not necessarily write to
./.build/databaseafter packaging. It may need to write to Electron's user data directory.Possible config:
The desktop package can expose helpers that resolve app-specific runtime paths:
This should be added carefully so normal web/server deployments do not inherit Electron assumptions.
14. Development Workflow
Expected developer workflow:
Expected production-style workflow:
The exact order may change if desktop build composes generation or database setup later, but the first version should stay explicit and consistent with existing Stackpress command flow.
15. Implementation Phases
15.1. Phase 1: Package Skeleton
packages/stackpress-desktop.desktop:dev,desktop:build, anddesktop:packageevents.desktopworkspace shortcut.DesktopConfig,DesktopPlugin,DesktopRouteRule, and menu registrytypes.
desktop.runtime,desktop.server, reserveddesktop.protocolsettings, rawdesktop.electronsettings, and updatersettings.
runtime: 'http'and return a clear unsupported-runtime error forruntime: 'protocol'.15.2. Phase 2: Local HTTP Runtime
desktopplugin.plugin-contributed config.
desktop:configbefore creating native Electron objects.BrowserWindow.15.3. Phase 3: Build Composition
build.15.4. Phase 4: Route Filtering
method matching, and default-blocked behavior once routes are configured.
15.5. Phase 5: Menu And Plugin Contributions
desktop:menubefore compiling the Electron menu.15.6. Phase 6: Template Proof
templates/blog/config/desktop.ts.15.7. Phase 7: Packaging
15.8. Future Phase: Updater Providers
15.9. Future Phase: WHATWG Protocol Runtime
stackpress/whatwgserver.Requestobjects.loading under the custom scheme.
or response objects.
16. Open Questions
stackpress-desktopuseelectron-builder,electron-forge, or expose both through config?Set, a priority-aware collection from@stackpress/lib, or a dedicated class?runtime: 'protocol'is enabled?desktop:buildrun generation automatically, or should it require existinggenerateandgenerate:clientcommands to have already run?17. Proposed First Milestone
The first useful milestone should be:
stackpress-desktoppackage existsdesktop:devstarts a Stackpress app inside Electrondesktop:buildcomposes existing Stackpress/Reactus build behaviorDesktopConfigandDesktopPlugintypes are available for inferencedesktopplugintemplates/blogcan run as a desktop appdesktop.routeswithout/admin/**This milestone proves the core design without committing too early to advanced
packaging, signing, updater provider implementation, or custom protocol behavior.
18. Discussion Prompt
Should Stackpress Desktop ship local HTTP mode as the first Electron runtime so existing Stackpress routing, backend handlers, sessions, API routes, server-rendered views, and lazy handlers continue to work?
If yes, the initial implementation can focus on
stackpress-desktop, route filtering, build composition, and atemplates/blogproof. The config can still reservedesktop.runtimeanddesktop.protocolso a future WHATWG custom protocol runtime has a stable place to land.Beta Was this translation helpful? Give feedback.
All reactions