DivisionBox API
DivisionBox API
Overview
DivisionBox is a lightweight sub-window system based on WebContentsView, used for plugin UI, tools, and debug panels.
Introduction
This document covers the currently shipped capabilities (open/close/state + lifecycle events). Advanced layouts are out of scope.
Permission note: DivisionBox is gated by the Permission Center. Plugins must request
window.create.
Core Concepts
Lifecycle states
prepare → attach → active → inactive → detach → destroy
| State | Description |
|---|---|
prepare | Preparing resources |
attach | Attached to window |
active | Active interaction |
inactive | Inactive, can be cached |
detach | Detached from window |
destroy | Destroyed and released |
DivisionBox config
interface DivisionBoxConfig {
url: string
title: string
icon?: string
size?: 'compact' | 'medium' | 'expanded'
keepAlive?: boolean
pluginId?: string
header?: {
show: boolean
title?: string
icon?: string
}
ui?: {
showInput?: boolean
inputPlaceholder?: string
showResults?: boolean
initialInput?: string
}
}
Shortcuts
| Shortcut | Action |
|---|---|
Command/Ctrl+D | Detach current item into DivisionBox |
Usage
Plugin SDK (recommended)
import { useDivisionBox } from '@talex-touch/utils/plugin/sdk'
const divisionBox = useDivisionBox()
const { sessionId } = await divisionBox.open({
url: 'https://example.com/tool',
title: 'My Tool',
size: 'medium',
keepAlive: true
})
const unsubscribe = divisionBox.onLifecycleChange((event) => {
console.log(event.sessionId, event.oldState, event.newState)
})
await divisionBox.close(sessionId)
unsubscribe()
Open from renderer
import { useTuffTransport } from '@talex-touch/utils/transport'
import { DivisionBoxEvents } from '@talex-touch/utils/transport/events'
const transport = useTuffTransport()
const response = await transport.send(DivisionBoxEvents.open, {
url: 'plugin://my-plugin/panel.html',
title: 'My Panel',
icon: 'ri:dashboard-line',
size: 'medium',
keepAlive: true,
pluginId: 'my-plugin'
})
Close
await divisionBox.close(sessionId, {
delay: 0,
animation: false,
force: false
})
Get session state
const response = await transport.send(DivisionBoxEvents.getState, { sessionId })
Update session state
await transport.send(DivisionBoxEvents.updateState, {
sessionId,
key: 'scrollY',
value: 150
})
API Reference
open(config) Opens a new DivisionBox window.
close(sessionId, options?)
Closes the session; force ignores keepAlive.
onStateChange(handler) Subscribes to simplified state changes.
onLifecycleChange(handler) Subscribes to full lifecycle transitions.
updateState(sessionId, key, value) Stores session state data.
getState(sessionId, key) Reads session state data.
URL Protocols
| Protocol | Description | Example |
|---|---|---|
plugin:// | Plugin assets | plugin://my-plugin/index.html |
file:// | Local files | file:///path/to/file.html |
http(s):// | Web resources | https://example.com |
tuff:// | Built-in pages | tuff://detached?itemId=xxx |
Flow Transfer Integration
{
"flowTargets": [
{
"id": "open-in-panel",
"name": "Open in panel",
"supportedTypes": ["json", "text"],
"featureId": "open-panel"
}
]
}
function onFeatureTriggered(featureId: string, query: TuffQuery) {
if (isFlowTriggered(query)) {
const flowData = extractFlowData(query)
divisionBox.open({
url: `/viewer.html?sessionId=${flowData.sessionId}`,
title: 'View Data'
})
}
}
IPC Channels
| Channel | Direction | Description |
|---|---|---|
division-box:open | Renderer → Main | Open session |
division-box:close | Renderer → Main | Close session |
division-box:get-state | Renderer → Main | Get state |
division-box:update-state | Renderer → Main | Update state |
division-box:get-active-sessions | Renderer → Main | List active sessions |
division-box:state-changed | Main → Renderer | State change notice |
division-box:session-destroyed | Main → Renderer | Session destroyed |
Resource Limits
Enforced by the main process in apps/core-app/src/main/modules/division-box/manager.ts; treat these as the authority rather than the numbers here.
| Limit | Value | Meaning |
|---|---|---|
MAX_ACTIVE_SESSIONS | 5 | Live sessions globally, matching the window pool. Opening past it throws DivisionBoxErrorCode.LIMIT_EXCEEDED. |
MAX_CACHED_SESSIONS | 5 | keepAlive sessions held in the LRU cache. Beyond it the least recently used is evicted and destroyed. |
MAX_VIEWS_PER_SESSION | 3 | Reserved upper bound. The current runtime flow attaches at most one view per session. |
Technical Notes
- DivisionBox is managed by the main process using
WebContentsView. - The SDK wraps IPC events and normalizes lifecycle updates.
Lifecycle in Detail
The state machine is prepare → attach → active → inactive → detach → destroy, declared as DivisionBoxState in packages/utils/types/division-box.ts.
prepare
The initial state of a session, between the open() call and window creation.
Entered when
divisionBox.open(config)is called and passes the preflight checks below. A rejected open never reaches this state.- The main process then assigns a session id and constructs the session.
Resources
- The global session cap is checked before anything is allocated.
- A session id is generated and the session registered in the session map.
- The config is validated and defaults applied; the plugin owner is resolved before any window is constructed.
- With
keepAlive, a state-change handler is installed. It does not put the session in the LRU cache yet — the cache entry is added when the session first reachesinactive, refreshed on return toactive, and removed ondestroy.
Failures
All three reject the open() call; none of them produce a session that then transitions.
- Global session cap reached →
DivisionBoxError(LIMIT_EXCEEDED), thrown before the session id is generated. - A UI view requested without an owning
pluginId, or with apluginIdthat resolves to no loaded plugin →DivisionBoxError(CONFIG_ERROR). - A non-authoritative caller requesting a UI view, or a
pluginIdthat does not match the calling plugin →DivisionBoxError(PERMISSION_DENIED), raised at the IPC boundary before the manager is reached.
attach
Reached once the window exists and the view is attached to it.
Entered when
- A
WebContentsViewis created and attached to the window. - The plugin UI begins loading (
did-start-loading).
Resources
- The view is created and the URL loaded.
- IPC listeners are registered.
- When detaching from CoreBox, ownership of the existing UI view is transferred rather than reloaded.
Failures
did-fail-load→ the session moves todestroy.render-process-gone→ the session moves todestroy.
active
The user is interacting with the DivisionBox.
Entered when
- The window takes focus.
- The page finishes loading (
did-finish-load). - Focus returns from
inactive.
Resources
- Steady state. IPC is safe to use here.
inactive
The window lost focus or is occluded.
Entered when
- The window emits
blur. - Another DivisionBox or CoreBox window covers it.
Resources
- With
keepAlive: false, deferred destruction may be scheduled. - With
keepAlive: true, resources are retained at lower priority. - The LRU cache may reclaim low-priority sessions from here.
Failures
- Prolonged inactivity can trigger memory-pressure reclamation.
- Past
MAX_CACHED_SESSIONS, the least recently used session is destroyed.
detach
The window closed, or the session was explicitly detached.
Entered when
divisionBox.close(sessionId)is called.- The user closes the window.
- A resource limit forces teardown.
Resources
- With
keepAlive: true, the session is already in the LRU cache from its firstinactive, and can be restored from there. - With
keepAlive: false, it proceeds straight todestroy. - The view and its associated resources are released.
Failures
- The close animation can be interrupted with
force: true. - A full cache means immediate destruction instead of caching.
destroy
Terminal state; resources are fully released.
Entered when
detachcompletes withkeepAlive: false.- A
keepAlivesession is evicted by the LRU cache. - Memory pressure or a resource limit forces teardown.
Resources
- All associated resources are released.
- The session is removed from the session map and the LRU cache.
- IPC listeners are unregistered.
division-box:session-destroyedis emitted.
Failures
- Nothing should throw here. A failed release is logged and does not block the transition — a stuck session would be worse than a leaked handle.
State transitions
Best Practices
- Enable
keepAlivefor frequently used panels. - Choose
sizebased on content density. - Persist user state via
updateState/getState. - Release resources on
inactiveanddestroystates.