Skip to content

Window API ​

client.window lets an app describe its document and control its own window: focus, minimize, maximize, restore, and ask to be closed. It is the smallest API in the SDK and the one every app should use.

Availability ​

No permission is required, and no connection: the authority is intrinsic, because every call acts on the window your app is running in and nothing else. An app cannot see or control another app's window.

Interface ​

ts
interface AppDocumentState { dirty: boolean; busy: boolean; title?: string }

interface AppWindowState {
  visible: boolean;
  focused: boolean;
  mode: "normal" | "maximized" | "tiled-left" | "tiled-right";
  canMaximize: boolean;
}

interface AppWindowAPI {
  setDocumentState(state: AppDocumentState): Promise<void>;
  getState(signal?: AbortSignal): Promise<AppWindowState>;
  focus(signal?: AbortSignal): Promise<void>;
  minimize(signal?: AbortSignal): Promise<void>;
  maximize(signal?: AbortSignal): Promise<void>;
  restore(signal?: AbortSignal): Promise<void>;
  requestClose(signal?: AbortSignal): Promise<void>;
}

setDocumentState is how the desktop learns three things it cannot see inside your frame:

  • dirty — there is unsaved work. The desktop warns before closing the window, the workspace or the app.
  • busy — an operation is in progress. Closing, disconnecting and quitting are blocked while it is true, so set it around real work and clear it in a finally.
  • title — an optional document name, shown in the title bar beside your app's name. Non-blank, at most 120 characters.

Examples ​

Track edits and saves:

ts
function markEdited(): void {
  void client.window.setDocumentState({ dirty: true, busy: false, title: currentFile });
}

async function save(): Promise<void> {
  await client.window.setDocumentState({ dirty: true, busy: true, title: currentFile });
  try {
    await writeSomewhere();
    await client.window.setDocumentState({ dirty: false, busy: false, title: currentFile });
  } catch (error) {
    await client.window.setDocumentState({ dirty: true, busy: false, title: currentFile });
    throw error;
  }
}

Adapt to the window instead of assuming a size:

ts
const state = await client.window.getState();
layout.compact = state.mode === "tiled-left" || state.mode === "tiled-right";
if (!state.focused) pausePolling();

Offer a close action that respects the desktop's guards:

ts
closeButton.addEventListener("click", () => void client.window.requestClose());

requestClose() acknowledges the request; it does not promise the window closes. If the document is dirty the desktop asks the user first, and your app may be unmounted before the promise settles — do the saving before you call it.

Errors ​

CodeMessageMeaning
invalidProvide dirty, busy and an optional window title.Wrong shape, or a title over 120 characters or blank.
invalidThis method takes no options.Arguments passed to a method that takes none.
unavailableWindow controls are unavailable.The host chrome is not available for this instance.
abortedWindow request canceled.The supplied signal fired.

Lifecycle and permissions ​

Calls affect only the calling window, and there is no way to address another instance — opening your app twice gives two windows that each control themselves.

The desktop combines your busy flag with its own: a running transfer, an open system dialog or a clipboard publication also marks the window busy. Your flag can add to that but cannot clear it.

State you set is not persisted. After a reconnect or a reopen, set it again from your own state as your app initialises.

ShellCanvas documentation