Introduction
Welcome to Chapter 6! So far, we’ve explored the foundational concepts of Puter.js, from its internal workings to interacting with its file system. Now, it’s time to bring our applications to life by understanding how they run within the Puter.js desktop environment and how to manage their visual interfaces – the windows!
In this chapter, you’ll learn how Puter.js treats applications as first-class citizens, allowing us to define, launch, and control them. We’ll dive deep into the Puter.js windowing system, discovering how to create, manipulate, and respond to events from application windows. Mastering these concepts is crucial for building interactive, multi-window experiences that feel native to the Puter.js operating system. Get ready to transform your code into dynamic, user-friendly applications!
To get the most out of this chapter, ensure you’re comfortable with basic JavaScript, have a working Puter.js development environment, and understand the core Puter.js APIs and file system interactions covered in previous chapters.
Core Concepts: Understanding Apps and Windows
Puter.js aims to provide a web-based operating system experience, which means applications aren’t just single web pages. They are programs that can launch, manage multiple windows, and interact with the desktop.
The Puter.js Operating System Abstraction
Imagine Puter.js as a virtual desktop that runs entirely in your browser. When you create an app for Puter.js, you’re essentially building a program that operates within this virtual environment. This abstraction allows your web-based applications to behave more like traditional desktop software, with features like multiple windows, taskbar integration, and a consistent user experience.
Apps vs. Processes
In Puter.js, an “app” refers to the defined program itself, complete with its code, assets, and configuration. When you launch an app, Puter.js creates one or more “processes” or “instances” of that app. Each running instance, particularly each distinct window, can be thought of as a separate process from the user’s perspective, though under the hood, they might share resources or communicate.
The Puter.js App Manifest: puter.json
Just like many modern application platforms, Puter.js uses a manifest file to describe your application. This file, typically named puter.json, lives at the root of your app’s directory and provides essential metadata to the Puter.js environment.
What’s in puter.json?
name: The display name of your application.version: The version number of your app.description: A brief explanation of what your app does.icon: The path to an icon file (e.g.,icon.png) that will represent your app on the desktop or in the taskbar.main: The entry point for your application (e.g.,index.htmlorindex.js). This is what Puter.js loads when your app starts.permissions: A crucial array defining what your app is allowed to do (e.g., access files, use network, etc.). We’ll cover permissions in detail in the next chapter, but it’s important to know they live here.windows: (Optional) Default configuration for windows your app might open.
Why is puter.json important?
It’s the blueprint for your app. Without it, Puter.js wouldn’t know how to launch your application, what to call it, or what resources it needs. It also plays a vital role in the Puter.js security model by explicitly declaring permissions.
The Puter.window Object: Your Window to the World
The Puter.window API is your primary tool for interacting with the visual interface of your application. It provides methods to create new windows, access the current window, and control various window properties.
Think of each window as a mini-browser instance running your app’s content.
Key Puter.window functionalities:
Puter.window.create(options): Opens a new window with specified properties.Puter.window.current(): Returns an object representing the window from which the current script is running.- Window Properties: Each window object has properties like
id,title,width,height,x,y,minimized,maximized,focused. - Window Methods: Control methods like
close(),minimize(),maximize(),restore(),focus(),move(x, y),resize(width, height),setTitle(newTitle). - Window Events: Listen for events such as
onClose,onFocus,onBlur.
Let’s visualize the relationship between an app, its manifest, and its windows:
Figure 6.1: Simplified Puter.js App and Window Lifecycle
App Lifecycle
A Puter.js app typically follows a lifecycle:
- Installation/Deployment: The app’s files and
puter.jsonare placed in the Puter.js file system. - Launch: The user clicks the app icon. Puter.js reads
puter.json, checks permissions, and launches themainentry point. - Execution: The app’s code runs. It can open new windows, interact with Puter.js APIs (like
Puter.fs), and handle user input. - Termination: The app closes all its windows, or the user explicitly quits the app from the taskbar. Puter.js cleans up associated processes.
Step-by-Step Implementation: Building Our First Multi-Window App
Let’s put these concepts into practice. We’ll create a simple “Hello Puter” app that can open a new, smaller window.
Step 1: Create Your App Directory and puter.json
First, create a new directory for your app. Let’s call it my-first-app. Inside this directory, create a file named puter.json.
# In your Puter.js environment's terminal, or your local dev setup
mkdir my-first-app
cd my-first-app
Now, create puter.json inside my-first-app:
// my-first-app/puter.json
{
"name": "My First App",
"version": "1.0.0",
"description": "A simple Puter.js app to demonstrate window management.",
"icon": "/system/apps/my-first-app/icon.png",
"main": "index.html",
"permissions": [
"window:create",
"window:current",
"storage:read",
"storage:write"
]
}
Explanation:
name,version,description: Standard metadata.icon: We’re pointing to anicon.pngwithin our app’s directory. For now, you can create a blankicon.pngor use a placeholder.main: Specifies thatindex.htmlwill be the starting point when our app launches.permissions: This is critical. We’re explicitly requestingwindow:createto allow our app to open new windows, andwindow:currentto access the current window.storage:readandstorage:writeare good general permissions for many apps. Remember: Always declare permissions your app needs. Puter.js will prompt the user if your app requests sensitive permissions.
Step 2: Create the Main Window’s HTML (index.html)
Next, create index.html in the my-first-app directory. This will be the content of our main application window.
<!-- my-first-app/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My First Puter.js App</title>
<style>
body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #f0f0f0; }
button { padding: 10px 20px; font-size: 16px; cursor: pointer; border: none; border-radius: 5px; background-color: #007bff; color: white; margin-top: 20px; }
button:hover { background-color: #0056b3; }
.message { font-size: 1.2em; color: #333; }
</style>
</head>
<body>
<h1 class="message">Hello from My First Puter.js App!</h1>
<button id="openNewWindowBtn">Open a New Window</button>
<script src="app.js"></script>
</body>
</html>
Explanation:
- A standard HTML structure.
- We’ve added a heading and a button.
- Crucially, we link to
app.jsat the end of the<body>tag. This script will contain our Puter.js window management logic.
Step 3: Implement Window Logic (app.js)
Now, create app.js in the same my-first-app directory. This script will handle opening the new window.
// my-first-app/app.js
// Wait for the DOM to be fully loaded before attaching event listeners
document.addEventListener('DOMContentLoaded', () => {
console.log('App script loaded and DOM content is ready.');
// Get a reference to our button
const openNewWindowBtn = document.getElementById('openNewWindowBtn');
// Check if the button exists before adding an event listener
if (openNewWindowBtn) {
openNewWindowBtn.addEventListener('click', async () => {
console.log('Open New Window button clicked!');
try {
// Use the Puter.window.create() API to open a new window
// This call is asynchronous, so we use 'await'
const newWindow = await Puter.window.create({
url: 'secondary.html', // The HTML file for the new window
title: 'Secondary Puter Window', // Title for the new window
width: 400, // Desired width
height: 300, // Desired height
resizable: true, // Allow resizing
minimized: false, // Start not minimized
maximized: false // Start not maximized
});
console.log('New window opened:', newWindow.id);
// We can also interact with the current window
const currentWindow = Puter.window.current();
if (currentWindow) {
currentWindow.setTitle('Main App - Window Opened!');
console.log('Current window title updated.');
}
} catch (error) {
console.error('Failed to open new window:', error);
// In a real app, you might show a user-friendly error message
alert(`Error opening window: ${error.message}`);
}
});
} else {
console.error('Button with ID "openNewWindowBtn" not found.');
}
});
Explanation:
- We use
DOMContentLoadedto ensure the button exists before trying to access it. Puter.window.create(): This is the core API call.url: 'secondary.html': This tells Puter.js to loadsecondary.html(which we’ll create next) into the new window. The URL is relative to the app’s root.title,width,height,resizable,minimized,maximized: These are standard options to configure the new window’s appearance and behavior.
await:Puter.window.create()is an asynchronous operation because window creation takes time. Weawaitits completion.Puter.window.current(): We demonstrate how to get a reference to the window whereapp.jsis running and update its title.
Step 4: Create the Secondary Window’s HTML (secondary.html)
Finally, create secondary.html in the my-first-app directory. This will be the content for the window that opens when the button is clicked.
<!-- my-first-app/secondary.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secondary Puter Window</title>
<style>
body { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background-color: #e6f7ff; }
h2 { color: #0056b3; }
button { padding: 8px 15px; font-size: 14px; cursor: pointer; border: none; border-radius: 4px; background-color: #dc3545; color: white; margin-top: 15px; }
button:hover { background-color: #c82333; }
</style>
</head>
<body>
<h2>This is a secondary window!</h2>
<p>You can manage multiple windows in your Puter.js app.</p>
<button id="closeWindowBtn">Close This Window</button>
<script>
document.addEventListener('DOMContentLoaded', () => {
const closeWindowBtn = document.getElementById('closeWindowBtn');
if (closeWindowBtn) {
closeWindowBtn.addEventListener('click', async () => {
console.log('Close button clicked in secondary window.');
try {
const currentWindow = Puter.window.current();
if (currentWindow) {
await currentWindow.close(); // Close the current window
console.log('Secondary window closed.');
}
} catch (error) {
console.error('Failed to close window:', error);
}
});
}
});
</script>
</body>
</html>
Explanation:
- A simple HTML page for the secondary window.
- It includes its own JavaScript to get the current window (
Puter.window.current()) and callcurrentWindow.close()when its button is clicked. This demonstrates closing a window programmatically.
Step 5: Run Your App!
To run this app:
- Ensure all three files (
puter.json,index.html,app.js, and a placeholdericon.png) are in themy-first-appdirectory. - In your Puter.js development environment, navigate to the
my-first-appdirectory. - Launch your app. The exact method depends on your Puter.js setup:
- If you have a Puter.js desktop environment: You might be able to simply open the directory, and the system will recognize
puter.jsonas an app. - If running via a command-line tool (e.g.,
puter-cli): You might use a command likeputer run my-first-apporputer launch .from within themy-first-appdirectory. (Please refer to your specific Puter.js CLI documentation for the precise command).
- If you have a Puter.js desktop environment: You might be able to simply open the directory, and the system will recognize
You should see your “My First Puter.js App” window appear. Click the “Open a New Window” button, and observe a new, smaller window appearing. You can then close the secondary window using its own button.
Mini-Challenge: The Dynamic Title App
Let’s enhance our understanding with a small challenge.
Challenge: Modify the “My First App” to include an input field in the main window. When the user types text into this field and presses Enter, the title of the main window should update to reflect the input text.
Hint:
- Add an
<input type="text" id="titleInput">element toindex.html. - In
app.js, get a reference to this input field. - Add an
keydownevent listener to the input field, specifically checking for theEnterkey. - Inside the event listener, get the value from the input field.
- Use
Puter.window.current().setTitle(newValue)to update the window title.
What to observe/learn: This challenge reinforces accessing the current window and dynamically changing its properties based on user input, a fundamental aspect of interactive applications.
Common Pitfalls & Troubleshooting
Window Fails to Open or App Doesn’t Launch:
- Check
puter.json: Is it correctly formatted JSON? Ismainpointing to the correct entry HTML/JS file? Arepermissionsforwindow:createandwindow:currentincluded? - File Paths: Ensure
urlinPuter.window.create()correctly points tosecondary.html(or any other resource). Paths are relative to your app’s root. - Console Errors: Always check the developer console (usually F12 in a browser) for JavaScript errors or Puter.js specific error messages. These often indicate permission issues or incorrect API usage.
- Check
PuterObject is Undefined:- This usually means your script is not running within the Puter.js environment, or the Puter.js SDK isn’t properly loaded or initialized. Ensure you’re launching your app through the Puter.js system as intended, not just opening
index.htmldirectly in a standard browser. - Verify the Puter.js environment is active and injected its global
Puterobject.
- This usually means your script is not running within the Puter.js environment, or the Puter.js SDK isn’t properly loaded or initialized. Ensure you’re launching your app through the Puter.js system as intended, not just opening
Window Properties Not Applying (e.g., wrong size/title):
- Double-check the options object passed to
Puter.window.create(). Typographical errors are common. - Ensure the Puter.js environment you’re running on is up-to-date and supports the specific options you’re using.
- Double-check the options object passed to
Summary
Congratulations! You’ve successfully navigated the world of Puter.js apps and window management. Here are the key takeaways from this chapter:
- Puter.js Apps are Structured: Applications are defined by a
puter.jsonmanifest file, which provides metadata, entry points, and crucial permissions. - Windows are Your UI: The
Puter.windowAPI is central to creating and managing the visual interfaces of your applications. Puter.window.create(): This powerful asynchronous method allows you to open new windows with customizable properties like URL, title, dimensions, and behavior.Puter.window.current(): Use this to get a reference to the window your script is currently running in, enabling dynamic control over its properties.- App Lifecycle: Understand how Puter.js launches, runs, and terminates your applications.
- Permissions are Key: Always declare necessary permissions in
puter.jsonto ensure your app can perform its intended actions.
In the next chapter, we’ll delve deeper into the critical topic of Permissions and Security Model, understanding how Puter.js protects user data and system integrity, and how to correctly request and handle permissions for your applications.
References
- HeyPuter/puter GitHub Repository
- Introduction to Web APIs - MDN Web Docs
- State Management in Vanilla JS: 2026 Trends - Medium
- JavaScript Best Practices: Tips for Writing Better Code in 2026 - Trio.dev
This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.