deskforge
v1.0.9
Published
Build everything. Own everything. Pay for nothing.
Readme
deskforge Framework Documentation
deskforge is a lightweight, component-oriented application framework for building screen-based UI trees and rendering them to images or other outputs. The current repository ships a small but practical foundation for:
- building a declarative node tree
- routing between screens
- rendering a tree with the renderer package
- exporting the output to PNG or other formats
The project is organized as a monorepo with separate packages for core rendering, routing, UI, state, theming, device integration, and more.
This documentation reflects the current implementation in this repository and focuses on the patterns demonstrated by the example app in examples/deskforge-app.
1. What deskforge is
deskforge is built around a simple idea:
- You describe your UI as a tree of nodes.
- Each node has a type, props, and children.
- A renderer walks that tree and draws it.
- A router lets you switch between screens.
This model is intentionally simple and close to the rendering pipeline used by many UI systems, but it is currently aimed at a desktop-style, image-rendering workflow rather than a full browser runtime.
Core principles
- Declarative UI composition using node trees
- Screen-based navigation with automatic route discovery
- App.ts as the landing entry for the app shell and screen tree
- Renderer-driven output for images and visual artifacts
- Modular packages that can be used independently
2. Repository structure
A typical deskforge workspace contains:
packages/
core/ # node tree, scheduler, component primitives
renderer/ # rendering engine
router/ # screen routing and navigation
ui/ # built-in UI primitives
state/ # state utilities
theme/ # theme tokens and styling
device/ # device integrations
...
examples/
deskforge-app/ # full example appImportant packages
@deskforge/core: defines the underlying node model and factory helpers@deskforge/renderer: renders the node tree to canvas-like output@deskforge/router: handles route registration and navigation@deskforge/ui: provides reusable UI building blocks
3. Installation
If you are using the monorepo directly, install dependencies from the repository root:
cd /path/to/deskforge
npm installIf you are building an app that depends on the published packages, install the relevant packages:
npm install @deskforge/core @deskforge/router @deskforge/rendererYou can also add the UI package:
npm install @deskforge/uiPublishing packages
The repository includes a package publish workflow for packages/*.
npm install
npm run publish:packagesThis builds all workspace packages and publishes each non-private package with public access.
To rebuild and publish with one command:
npm run build && npm publish --access publicTo scaffold a new app from the published CLI:
npx create-deskforge-app my-app
cd my-app
npm install
npm dfcli start4. Current authoring syntax
The current deskforge examples use a functional, no-JSX authoring style. Components are written as pure functions that return a DFNode tree created with helpers such as createNode and the built-in UI primitives.
import {
View,
Text,
StyleSheet,
Button,
SwiftUI,
AndroidUI,
} from '@deskforge/ui/src'
import { State } from '@deskforge/state/src'
const styles = StyleSheet.create({
container: { flex: 1, padding: 24, justifyContent: 'center' },
title: { fontSize: 24, fontWeight: '600', marginBottom: 12 },
})
export function CounterScreen() {
const state = State({ count: 0 })
return View(styles.container, [
Text(`Count: ${state.count}`, styles.title),
Button('Increment', { padding: 12 }, {
onPress: () => {
state.count += 1
},
}),
])
}Key patterns in the current codebase:
- No JSX or HTML-style tags are used.
- UI is composed with functional helpers such as View, Text, Pressable, ScrollView, TextInput and Modal.
- State is created with State(...) and updated directly.
- Styling is declared with StyleSheet.create(...) using React Native-style objects.
- Theme usage is imported from @deskforge/theme/src and applied with Theme.apply(...).
5. Packages and what they are for
deskforge is organized as a set of focused packages. Each package can be used independently, and they work together through the core node-tree model.
Core packages
- @deskforge/core
- Core node model, createNode, diff engine, scheduler, and component primitives.
- Use this when you want to build a UI tree or define custom components.
import { createNode } from '@deskforge/core'
const node = createNode('view', { style: { padding: 16 } }, [])- @deskforge/renderer
- Renders node trees to a canvas-style surface and exports to image/buffer output.
- Use this to display or export your UI.
import { Renderer } from '@deskforge/renderer'
const renderer = new Renderer({ width: 800, height: 600, backgroundColor: '#fff' })
renderer.renderNode(node)
await renderer.saveAs('./dist/output.png')- @deskforge/router
- Screen routing, navigation helpers, and automatic file-based route discovery.
- Use this when your app needs multiple screens and navigation.
import { Router } from '@deskforge/router'
// routes are discovered from the file structure automatically
Router.start()- @deskforge/ui
- A modern set of functional UI primitives for layout, text, input, lists, overlays, feedback, and platform-aware controls.
- The current examples use the monorepo entry point with /src imports, including View, Text, Pressable, Button, TouchableOpacity, Image, TextInput, ScrollView, FlatList, SectionList, SafeAreaView, KeyboardAvoidingView, Modal, ActivityIndicator, Switch, Slider, CheckBox, StatusBar, Platform, SwiftUI, and AndroidUI.
import {
View,
Text,
ScrollView,
Modal,
StyleSheet,
SwiftUI,
AndroidUI,
LiquidGlass,
} from '@deskforge/ui/src'State and styling packages
- @deskforge/state
- Reactive state helpers for local app state and component state.
import { State } from '@deskforge/state'
const state = State({ count: 0 })- @deskforge/theme
- Theme tokens, color palettes, spacing, animation helpers, and a simple Theme API for switching modes and applying theme preferences.
import { Theme } from '@deskforge/theme/src'
Theme.apply({ isDark: true })Device and platform integrations
- @deskforge/device
- Device APIs such as camera, contacts, sensors, and media helpers.
import { Camera, Contacts } from '@deskforge/device'- @deskforge/file
- File system helpers for reading and writing files.
import { readFile, writeFile } from '@deskforge/file'- @deskforge/network
- Peer-to-peer networking helpers, mDNS discovery, and TCP transfer utilities.
import { discoverPeers } from '@deskforge/network'- @deskforge/notifications
- Toasts, push notifications, SMS, email, and notification helpers.
import { Notify } from '@deskforge/notifications'- @deskforge/security
- Biometric helpers and encryption utilities.
import { encrypt } from '@deskforge/security'AI, data, and app platform packages
- @deskforge/ai
- Local AI helpers and brain-style node creation for on-device reasoning.
import { createBrain } from '@deskforge/ai'- @deskforge/db
- Database adapters for SQLite, Postgres, MySQL, and MongoDB.
import { connect } from '@deskforge/db'- @deskforge/background
- Background tasks and scheduling helpers.
import { BackgroundTask } from '@deskforge/background'- @deskforge/updater
- Self-hosted automatic update helpers.
import { Updater } from '@deskforge/updater'- @deskforge/payments
- Unified payment wrapper with failover support for common providers.
import { Pay } from '@deskforge/payments'- @deskforge/i18n
- Localization helpers with African language support.
import { t } from '@deskforge/i18n'- @deskforge/logger
- Structured logging helpers for the framework and app code.
import { logger } from '@deskforge/logger'- @deskforge/bridge
- Language bridge for calling code written in Python, Go, Rust, PHP, C, Kotlin, or Swift.
import { callPython } from '@deskforge/bridge'- @deskforge/cli
- Command-line tools for scaffolding and app management.
npx create-deskforge-app my-app
npm dfcli start5. Quick start: File-based routing
deskforge now includes a file-based router similar to Expo Router and Next.js. Routes are discovered automatically from your screen files, so you do not manually register them. Your App.ts file acts as the landing page and app shell for the discovered screens:
import { FileRouter, ManifestBuilder, Screen } from '@deskforge/router'
const HomeRoute = Screen(() => createNode('view', {}, []), {
title: 'Home',
layout: 'app',
})
const routes = new ManifestBuilder()
.add('index.ts', HomeRoute.component)
.add('about.ts', AboutScreen)
.add('note/[id].ts', NoteScreen)
.build()
const router = new FileRouter(routes)
router.navigate('/')
router.navigate('/note/123')
const node = router.render()
renderer.renderNode(node)See FILE_BASED_ROUTER.md for detailed documentation and examples.
5. Core concepts
5.1 The node tree
The building block of deskforge is a node. Every UI element is represented by a node with:
type: the component type, such astext,view, or a custom componentprops: properties that describe how the node should appear or behavechildren: nested nodes
The core package exposes createNode to construct nodes.
import { createNode } from '@deskforge/core'
const node = createNode('view', {
style: { background: '#ffffff', padding: 16 },
}, [])A more realistic example looks like this:
import { createNode } from '@deskforge/core'
const title = createNode('text', {
text: 'Hello deskforge',
color: '#111827',
font: '24px sans-serif',
})
const layout = createNode('view', {
style: { padding: 24 },
}, [title])5.2 The renderer
The renderer walks the node tree and produces visual output. In the current example app, the rendering flow is:
- create or compose a node tree
- pass it to the renderer
- save or display the result
import { Renderer } from '@deskforge/renderer'
const renderer = new Renderer({
width: 800,
height: 600,
backgroundColor: '#f7f8fb',
})
renderer.renderNode(layout)
await renderer.saveAs('./dist/output.png')5.3 Screens and routes
deskforge uses automatic route discovery from a screen folder structure. A screen is simply a function that returns a node tree, and it can also carry metadata such as a title and layout. The App.ts entry file becomes the landing page that hosts the app shell and initial screen.
import { Screen } from '@deskforge/router'
function HomeScreen() {
return createNode('view', {
style: { padding: 24 },
}, [])
}
const HomeRoute = Screen(HomeScreen, { title: 'Home', layout: 'app' })The router discovers screens automatically from the file structure:
import { Router } from '@deskforge/router'
Router.start()And navigate with:
Router.go('home')6. Building an app in deskforge
The simplest app has three parts:
- an entry point file, usually
App.ts, which acts as the landing page and app shell - one or more screen files discovered automatically by the router
- a small UI helper layer for creating views and text blocks
The example app in examples/deskforge-app follows this pattern.
6.1 Create the app entry point
A basic entry file looks like this:
import * as fs from 'fs'
import { Renderer } from '@deskforge/renderer'
import { Router } from '@deskforge/router'
import { HomeScreen } from './src/screens'
Router.start()
function createRoot() {
return createNode('view', {
style: { background: '#f7f8fb', padding: 24 },
}, [])
}
async function main() {
fs.mkdirSync('./dist', { recursive: true })
const renderer = new Renderer({
width: 800,
height: 600,
backgroundColor: '#f7f8fb',
})
Router.go('home')
renderer.renderNode(createRoot())
await renderer.saveAs('./dist/app.png')
}
main().catch(console.error)6.2 Create screen files
A screen is a function that returns a node tree. The example uses a simple Screen helper to create a container.
import type { ScreenParams } from '@deskforge/router'
import { Screen, Text, View } from '../ui/Responsive'
export default function HomeScreen(params: ScreenParams = {}) {
return Screen({ style: { background: '#f8fafc', padding: 24 } })
.add(
View({ style: { background: '#ffffff', borderRadius: 24, padding: 24 } }),
Text('Home Screen', { style: { fontSize: 28, fontWeight: '700' } }),
Text('Welcome to deskforge.', { style: { color: '#4b5563', fontSize: 16 } }),
)
.build()
}6.3 App entry and automatic screen discovery
The App.ts entry file serves as the landing page and initial app shell. Screen files under the app structure are discovered automatically and exposed by the router.
import { Router } from '@deskforge/router'
Router.start()6.4 Navigate between screens
Router.go('details', { noteId: 'A1' })The current router stores navigation history and exposes helpers such as:
Router.go(name, params)Router.back()Router.forward()Router.params()Router.currentRoute()Router.start()
For a React Navigation-like experience, deskforge also supports a stack-style navigation model with screen components, route params, and a shared app shell defined in App.ts.
6. Using the example app
The repository includes an example app in examples/deskforge-app. It demonstrates how to build a basic screen-based app.
Run the example
cd examples/deskforge-app
npm install
npm run build
npm dfcli startThis example renders images for the home and details screens into the dist folder.
Example layout
examples/deskforge-app/
App.ts
src/
screens/
Home.ts
Details.ts
index.ts
ui/
Responsive.ts7. Creating your own UI layers
Because deskforge is still evolving, the most practical way to build UI is to define your own lightweight helper functions on top of the core node API.
Example: simple text and view helpers
import { createNode } from '@deskforge/core'
function Text(value: string, props: Record<string, unknown> = {}) {
return createNode('text', {
text: value,
...props,
})
}
function View(props: Record<string, unknown> = {}, children: any[] = []) {
return createNode('view', props, children)
}This pattern keeps your app code readable while staying close to the underlying node tree model.
8. Styling and layout
The current implementation uses inline style objects and passes them through as node props. A simple screen might look like this:
const card = createNode('view', {
style: {
background: '#ffffff',
borderRadius: 24,
padding: 24,
boxShadow: '0 10px 20px rgba(0,0,0,0.08)',
},
}, [
createNode('text', { text: 'Card title', font: '700 24px sans-serif' }),
])Best practices
- keep styles in a single place when the app grows
- use small helper components for repeated layout patterns
- keep screen logic separate from presentation
- treat each screen as a pure function that returns a node tree
9. Rendering pipeline
deskforge follows a straightforward pipeline:
Screen function -> Node tree -> Renderer -> PNG / buffer / outputWhat happens internally
- A screen returns a node tree.
- The node tree is composed of parent and child nodes.
- The renderer visits each node.
- The renderer converts node props into drawing commands.
- The output is saved or displayed.
This makes it easy to reason about your app as a tree of visual elements.
10. How to structure a real project
A practical project structure may look like this:
my-app/
package.json
tsconfig.json
src/
App.ts
screens/
Home.ts
Details.ts
Settings.ts
ui/
Responsive.ts
components/
Card.ts
Button.tsSuggested workflow
- Create a main
App.tsfile as the landing page and app shell. - Create one file per screen.
- Create helper modules for common UI primitives.
- Let the router discover screens automatically.
- Render the current screen through the renderer.
11. Extending deskforge
The framework is modular, so you can extend it in several ways:
Add custom components
Create a new node type and teach the renderer how to paint it. This is the most direct way to introduce custom visuals.
Add custom painters
The renderer package supports custom painters. If you need a new visual primitive, register a painter for it.
Add new packages
The monorepo already includes packages for state, theme, device support, notifications, and more. You can build on those modules to grow the framework into a bigger app platform.
12. Building a complete app example
Here is a complete example that follows the patterns already used in the repository.
import * as fs from 'fs'
import { Renderer } from '@deskforge/renderer'
import { Router, Screen } from '@deskforge/router'
import { Text, View, Window } from './src/ui/Responsive'
Router.start()
function createRoot() {
return Window({ style: { background: '#f7f8fb', padding: 24 } })
.add(
View({ style: { background: '#ffffff', borderRadius: 32, padding: 24 } }),
Text('My App Shell', { style: { fontSize: 24, fontWeight: '700' } }),
Router.render() ?? Text('No screen selected.', { style: { color: '#6b7280', fontSize: 16 } }),
)
.build()
}
async function main() {
fs.mkdirSync('./dist', { recursive: true })
const renderer = new Renderer({
width: 900,
height: 700,
backgroundColor: '#f7f8fb',
})
Router.go('home')
renderer.renderNode(createRoot())
await renderer.saveAs('./dist/demo.png')
}
main().catch(console.error)13. Tips and best practices
- Keep screens focused on one responsibility.
- Compose small building blocks instead of writing large monolithic screens.
- Use helper functions to hide repetitive node creation.
- Keep route names stable and descriptive.
- Keep rendering logic separate from navigation logic.
- Use App.ts as the shared landing page and shell for the app.
- Let the router discover screens automatically instead of manually registering them.
- Use
dist/for generated assets so your source stays clean.
14. Current limitations and roadmap
The current repository is a solid foundation, but it is still early-stage. Some areas are intentionally simple and may evolve over time:
- the renderer is focused on practical visual output
- the router is lightweight and screen-based
- custom components and advanced layout systems are still being expanded
- additional package features may be added as the framework grows
That said, the architecture is already good enough for building small, structured apps and rendering them in a controlled pipeline.
15. Summary
deskforge is best understood as a small, modular framework for building UI trees and rendering them as visual output. The core workflow is:
- define a node tree
- compose screens
- register routes
- render the active screen
- export the result
If you follow the structure used by the example app, you can build a simple and maintainable app very quickly.
16. Next steps
To continue learning the framework:
- read the example app in
examples/deskforge-app - inspect the core packages in
packages/core,packages/router, andpackages/renderer - start with a single screen and build outward from there
- add custom helpers for text, cards, buttons, and layouts as your app grows
