Docs/DivisionBox API

DivisionBox API

Universal Developer

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

EXAMPLE.VUE
prepare → attach → active → inactive → detach → destroy
StateDescription
preparePreparing resources
attachAttached to window
activeActive interaction
inactiveInactive, can be cached
detachDetached from window
destroyDestroyed and released

DivisionBox config

EXAMPLE.TYPESCRIPT
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

ShortcutAction
Command/Ctrl+DDetach current item into DivisionBox

Usage

Plugin SDK (recommended)

EXAMPLE.TYPESCRIPT
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

EXAMPLE.TYPESCRIPT
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

EXAMPLE.TYPESCRIPT
await divisionBox.close(sessionId, {
  delay: 0,
  animation: false,
  force: false
})

Get session state

EXAMPLE.TYPESCRIPT
const response = await transport.send(DivisionBoxEvents.getState, { sessionId })

Update session state

EXAMPLE.TYPESCRIPT
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

ProtocolDescriptionExample
plugin://Plugin assetsplugin://my-plugin/index.html
file://Local filesfile:///path/to/file.html
http(s)://Web resourceshttps://example.com
tuff://Built-in pagestuff://detached?itemId=xxx

Flow Transfer Integration

EXAMPLE.JSON
{
  "flowTargets": [
    {
      "id": "open-in-panel",
      "name": "Open in panel",
      "supportedTypes": ["json", "text"],
      "featureId": "open-panel"
    }
  ]
}
EXAMPLE.TYPESCRIPT
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

ChannelDirectionDescription
division-box:openRenderer → MainOpen session
division-box:closeRenderer → MainClose session
division-box:get-stateRenderer → MainGet state
division-box:update-stateRenderer → MainUpdate state
division-box:get-active-sessionsRenderer → MainList active sessions
division-box:state-changedMain → RendererState change notice
division-box:session-destroyedMain → RendererSession 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.

LimitValueMeaning
MAX_ACTIVE_SESSIONS5Live sessions globally, matching the window pool. Opening past it throws DivisionBoxErrorCode.LIMIT_EXCEEDED.
MAX_CACHED_SESSIONS5keepAlive sessions held in the LRU cache. Beyond it the least recently used is evicted and destroyed.
MAX_VIEWS_PER_SESSION3Reserved 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 reaches inactive, refreshed on return to active, and removed on destroy.

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 a pluginId that resolves to no loaded plugin → DivisionBoxError(CONFIG_ERROR).
  • A non-authoritative caller requesting a UI view, or a pluginId that 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 WebContentsView is 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 to destroy.
  • render-process-gone → the session moves to destroy.

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 first inactive, and can be restored from there.
  • With keepAlive: false, it proceeds straight to destroy.
  • 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

  • detach completes with keepAlive: false.
  • A keepAlive session 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-destroyed is 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

stateDiagram-v2 [*] --> prepare: open() prepare --> attach: window created attach --> active: window focused active --> inactive: window blurred inactive --> active: window refocused inactive --> detach: close() / reclaim active --> detach: close() / reclaim attach --> detach: close() / reclaim detach --> [*]: keepAlive=false detach --> prepare: keepAlive=true, restored prepare --> destroy: load failure attach --> destroy: load failure / crash active --> destroy: crash inactive --> destroy: LRU eviction / memory pressure destroy --> [*]

Best Practices

  • Enable keepAlive for frequently used panels.
  • Choose size based on content density.
  • Persist user state via updateState/getState.
  • Release resources on inactive and destroy states.