Skip to content

LLM UI Spec - Desktop Platform

Preview: This specification is under active development and may change.

Platform-specific elements and patterns for desktop applications.

See Introduction for shared concepts (entities, components, prompts, etc.).


Overview

Desktop specs use file pattern *.desktop.spec.xml.

Key characteristics:

  • Menu bar with keyboard shortcuts
  • Toolbar with action buttons
  • Multi-window support
  • Context menus (right-click)
  • Resizable split views and panels
  • Status bar
  • Dialog boxes
  • Drag and drop
  • System tray integration
  • Tree view navigation

Common Attributes

AttributeTypeDescription
namestringIdentifier for referencing
typestringElement or container type
promptstringNatural language behavior description
usereferenceData binding
ifexpressionRender only if true
if-notexpressionRender only if false
for-eachcollectionIterate over items

All attributes (except name) can be expressed in prompt:

xml
<!-- Attribute syntax -->
<element type="text" use="@state.title" />
<element type="spinner" if="@state.loading" />
<container type="panel" for-each="@state.items" />

<!-- Prompt syntax (equivalent) -->
<element type="text" prompt="use @state.title" />
<element type="spinner" prompt="if @state.loading" />
<container type="panel" prompt="for each @state.items" />

<!-- Type in prompt -->
<element prompt="text, use @state.title" />
<element prompt="spinner, if @state.loading" />
<container prompt="panel, for each @state.items" />

Type is optional when parent context makes it clear:

xml
<!-- Inside menu, children are menu items -->
<container type="menu" prompt="label File">
  <element prompt="shortcut Ctrl+N, on click open @dialog.Create">New</element>
  <element prompt="shortcut Ctrl+S, on click @action.save">Save</element>
</container>

<!-- Inside tree-view, children are tree items -->
<container type="tree-view">
  <element prompt="home icon, on click @action.setView dashboard">Dashboard</element>
  <container prompt="folder icon, expanded">Projects</container>
</container>

<!-- Inside toolbar, children are toolbar buttons -->
<container type="toolbar">
  <element prompt="plus icon, tooltip New, on click open @dialog.Create" />
</container>

App Configuration

xml
<config>
  <window prompt="title MyApp, width 1200, height 800, min width 800, min height 600, hidden title bar" />
  <tray prompt="icon app-icon, tooltip MyApp" />
</config>

Container Types

Layout Types

TypeDescription
columnVertical layout
rowHorizontal layout
split-viewResizable split layout
panelSplit view panel
scrollScrollable container
xml
<container type="column" prompt="height 100%">
  <!-- Vertical content -->
</container>

<container type="row" prompt="padding small, gap medium, align center">
  <!-- Horizontal content -->
</container>

<container type="split-view" prompt="horizontal, grow">
  <container type="panel" prompt="width 260px, min width 200px, max width 400px, resizable, collapsible">
    <!-- Sidebar -->
  </container>
  <container type="panel" prompt="grow">
    <!-- Main content -->
  </container>
  <container type="panel" prompt="width 300px, resizable, collapsible" if="@state.propertiesVisible">
    <!-- Properties panel -->
  </container>
</container>

<container type="scroll" prompt="padding large">
  <!-- Scrollable content -->
</container>

Structural Types

TypeDescription
title-barCustom window title bar
toolbarAction toolbar
status-barApplication status bar
formForm container
xml
<!-- Custom title bar for frameless window -->
<container type="title-bar" prompt="draggable">
  <container type="row" prompt="padding small, gap medium, align center">
    <element type="icon" prompt="app-icon, size small" />
    <element type="text" prompt="body, medium weight">MyApp</element>
    <element type="spacer" />
    <element type="window-controls" />
  </container>
</container>

<!-- Toolbar -->
<container type="toolbar">
  <container prompt="group">
    <element prompt="plus icon, tooltip New (Ctrl+N), on click open @dialog.Create" />
    <element prompt="folder icon, tooltip Open (Ctrl+O), on click @action.open" />
    <element prompt="save icon, tooltip Save (Ctrl+S), on click @action.save, disabled if not @state.isDirty" />
  </container>
  <element type="separator" />
  <container prompt="group">
    <element prompt="undo icon, tooltip Undo, on click @action.undo, disabled if not @state.canUndo" />
    <element prompt="redo icon, tooltip Redo, on click @action.redo, disabled if not @state.canRedo" />
  </container>
  <element type="spacer" />
  <element type="search" prompt="placeholder Search..., width 250px, on search @action.search" />
</container>

<!-- Status bar -->
<container type="status-bar">
  <element use="@state.itemCount">items</element>
  <element type="spacer" />
  <element if="@state.isDirty">
    <element prompt="caption, muted">Unsaved changes</element>
  </element>
  <element if-not="@state.isDirty">
    <element prompt="caption, muted">Saved</element>
  </element>
</container>

Element Types

Display Elements

TypeDescription
textText display
iconIcon display
badgeBadge indicator
spinnerLoading spinner
spacerFlexible space
xml
<element type="text" prompt="heading1">Dashboard</element>
<element type="text" prompt="body, muted" use="@item.description" />
<element type="icon" prompt="app-icon, size small" />
<element type="badge" use="@state.status" prompt="size extra small" />
<element type="spinner" prompt="size extra small, if @state.syncing" />
<element type="spacer" />

Input Elements

TypeDescription
inputText input
textareaMulti-line input
searchSearch field
checkboxCheckbox toggle
selectDropdown select
xml
<element type="input" prompt="required, auto focus">Name</element>
<element type="textarea" prompt="rows 3">Description</element>
<element type="search" prompt="placeholder Search..., width 250px, on search @action.search" />
<element type="checkbox" use="@state.enabled">Enable feature</element>
<element type="select" prompt="options @state.options, on change @action.setOption" />

Action Elements

TypeDescription
buttonClick button
toolbar-buttonToolbar action
window-controlsMin/max/close buttons
xml
<element type="button" prompt="style primary, on click @action.save">Save</element>
<element type="button" prompt="style outline, on click close dialog">Cancel</element>
<element type="button" prompt="style danger, on click @action.delete then close dialog">Delete</element>
<element type="toolbar-button" prompt="plus icon, tooltip New, on click open @dialog.Create" />
<element type="window-controls" />

xml
<container type="menu-bar">
  <container type="menu" prompt="label File">
    <element prompt="shortcut Ctrl+N, on click open @dialog.Create">New</element>
    <element prompt="shortcut Ctrl+O, on click @action.open">Open...</element>
    <element type="separator" />
    <element prompt="shortcut Ctrl+S, on click @action.save, enabled if @state.isDirty">Save</element>
    <element prompt="shortcut Ctrl+Shift+S, on click @action.saveAs">Save As...</element>
    <element type="separator" />
    <container prompt="submenu">Import
      <element prompt="on click @action.importJSON">From JSON</element>
      <element prompt="on click @action.importCSV">From CSV</element>
    </container>
    <container prompt="submenu">Export
      <element prompt="on click @action.exportJSON">As JSON</element>
      <element prompt="on click @action.exportPDF">As PDF</element>
    </container>
    <element type="separator" />
    <element prompt="shortcut Ctrl+Comma, on click open @window.Settings">Settings</element>
    <element type="separator" />
    <element prompt="shortcut Alt+F4, on click @action.quit">Exit</element>
  </container>

  <container type="menu" prompt="label Edit">
    <element prompt="shortcut Ctrl+Z, on click @action.undo, enabled if @state.canUndo">Undo</element>
    <element prompt="shortcut Ctrl+Shift+Z, on click @action.redo, enabled if @state.canRedo">Redo</element>
    <element type="separator" />
    <element prompt="shortcut Ctrl+X, on click @action.cut">Cut</element>
    <element prompt="shortcut Ctrl+C, on click @action.copy">Copy</element>
    <element prompt="shortcut Ctrl+V, on click @action.paste">Paste</element>
    <element prompt="shortcut Delete, on click @action.delete">Delete</element>
    <element type="separator" />
    <element prompt="shortcut Ctrl+A, on click @action.selectAll">Select All</element>
  </container>

  <container type="menu" prompt="label View">
    <element prompt="shortcut Ctrl+B, on click @action.toggleSidebar, checked if @state.sidebarVisible">Toggle Sidebar</element>
    <element type="separator" />
    <element prompt="shortcut Ctrl+Equals, on click @action.zoomIn">Zoom In</element>
    <element prompt="shortcut Ctrl+Minus, on click @action.zoomOut">Zoom Out</element>
    <element prompt="shortcut Ctrl+0, on click @action.zoomReset">Reset Zoom</element>
    <element type="separator" />
    <element prompt="shortcut F11, on click @action.toggleFullScreen">Full Screen</element>
  </container>

  <container type="menu" prompt="label Help">
    <element prompt="shortcut F1, on click @action.openDocs">Documentation</element>
    <element prompt="shortcut Ctrl+Slash, on click open @dialog.Shortcuts">Keyboard Shortcuts</element>
    <element type="separator" />
    <element prompt="on click open @dialog.About">About</element>
  </container>
</container>

Context Menus

Right-click context menus.

xml
<context-menus>
  <context-menu name="Item">
    <element prompt="edit icon, on click @action.edit with @context.id">Edit</element>
    <element prompt="copy icon, shortcut Ctrl+D, on click @action.duplicate with @context.id">Duplicate</element>
    <element type="separator" />
    <element prompt="arrow up icon, on click @action.moveUp with @context.id, enabled if @context.index > 0">Move Up</element>
    <element prompt="arrow down icon, on click @action.moveDown with @context.id">Move Down</element>
    <element type="separator" />
    <element prompt="trash icon, style danger, shortcut Delete, on click @action.delete with @context.id">Delete</element>
  </context-menu>
</context-menus>

Using Context Menus

xml
<container type="row" prompt="context menu @contextMenu.Item, on double click @action.edit">
  <element type="text" use="@item.name" />
</container>

<!-- Or programmatically -->
<element type="button" prompt="on click show context menu @contextMenu.Item with @item">Options</element>

Tree View

Sidebar navigation with hierarchy.

xml
<container type="tree-view">
  <element prompt="home icon, selected if @state.view == dashboard, on click @action.setView dashboard">Dashboard</element>
  <container prompt="folder icon, expanded">Projects
    <element prompt="file icon, selected if @state.selectedProject == @item.id, on click @action.selectProject with @item.id, context menu @contextMenu.Project" for-each="@state.projects" use="@item.name" />
  </container>
  <element prompt="settings icon, on click open @window.Settings">Settings</element>
</container>

Views

Main content views (replaces pages).

xml
<views>
  <view name="Dashboard">
    <data>
      <query name="items" type="@entity.Item[]" />
    </data>

    <container type="scroll" prompt="padding large">
      <container type="column" prompt="gap large, max width 1200px">
        <element type="text" prompt="heading1">Dashboard</element>
        <!-- View content -->
      </container>
    </container>
  </view>

  <view name="Editor" prompt="param id">
    <container type="split-view" prompt="horizontal">
      <container type="panel" prompt="width 220px, border right">
        <!-- Palette -->
      </container>
      <container type="panel" prompt="grow">
        <!-- Canvas -->
      </container>
    </container>
  </view>
</views>

Windows

Multi-window support for secondary windows.

xml
<windows>
  <window name="Settings" prompt="title Settings, width 700, height 500, not resizable">
    <container type="row" prompt="height 100%">
      <container type="column" prompt="width 200px, background gray 50, padding medium">
        <element type="button" prompt="style subtle, align left, on click @action.setTab general">General</element>
        <element type="button" prompt="style subtle, align left, on click @action.setTab appearance">Appearance</element>
        <element type="button" prompt="style subtle, align left, on click @action.setTab account">Account</element>
      </container>

      <container type="column" prompt="grow, padding large">
        <container if="@state.settingsTab == general">
          <!-- General settings -->
        </container>
        <container if="@state.settingsTab == appearance">
          <!-- Appearance settings -->
        </container>
        <container if="@state.settingsTab == account">
          <!-- Account settings -->
        </container>
      </container>
    </container>
  </window>

  <window name="Preview" prompt="title Preview, width 500, height 700">
    <container type="toolbar">
      <element type="toolbar-button" prompt="smartphone icon, tooltip Mobile, on click @action.setSize mobile" />
      <element type="toolbar-button" prompt="tablet icon, tooltip Tablet, on click @action.setSize tablet" />
      <element type="toolbar-button" prompt="monitor icon, tooltip Desktop, on click @action.setSize desktop" />
    </container>
    <container prompt="grow">
      <!-- Preview content -->
    </container>
  </window>
</windows>

Dialogs

Modal dialog boxes.

xml
<dialogs>
  <dialog name="Create" prompt="title Create New Item, width 450">
    <container type="form" prompt="on submit @action.create, on success close dialog">
      <container type="column" prompt="gap medium, padding large">
        <element type="input" prompt="required, auto focus">Name</element>
        <element type="textarea" prompt="rows 3">Description</element>

        <container type="row" prompt="justify end, gap small, margin top medium">
          <element type="button" prompt="style outline, on click close dialog">Cancel</element>
          <element type="button" prompt="submit, style primary">Create</element>
        </container>
      </container>
    </container>
  </dialog>

  <dialog name="ConfirmDelete" prompt="title Delete Item?, width 400">
    <container type="column" prompt="padding large, gap medium">
      <element type="text">Are you sure you want to delete this item? This action cannot be undone.</element>
      <container type="row" prompt="justify end, gap small">
        <element type="button" prompt="style outline, on click close dialog">Cancel</element>
        <element type="button" prompt="style danger, on click @action.delete then close dialog">Delete</element>
      </container>
    </container>
  </dialog>

  <dialog name="About" prompt="title About MyApp, width 400">
    <container type="column" prompt="align center, gap medium, padding extra large">
      <element type="icon" prompt="app-icon, size extra large" />
      <element type="text" prompt="heading2">MyApp</element>
      <element type="text" prompt="body, muted">Version 1.0.0</element>
      <element type="button" prompt="style outline, on click close dialog">Close</element>
    </container>
  </dialog>
</dialogs>

Drag and Drop

xml
<!-- Draggable item -->
<container type="draggable" prompt="type item, data @item">
  <container type="card" prompt="padding small">
    <element type="text" use="@item.name" />
  </container>
</container>

<!-- Drop zone -->
<container type="drop-zone" prompt="accept item, on drop @action.handleDrop">
  <container prompt="padding medium, border dashed">
    <element type="text" prompt="muted">Drop items here</element>
  </container>
</container>

<!-- Reorderable list -->
<container type="list" prompt="reorderable, on reorder @action.reorder" for-each="@state.items">
  <container type="draggable" prompt="type item, data @item, reorderable">
    <container type="list-item">
      <element type="text" use="@item.name" />
    </container>
  </container>
</container>

Actions

Reusable action definitions.

xml
<actions>
  <action name="save" prompt="save current document, show success toast" />
  <action name="open" prompt="show file picker, load selected file" />
  <action name="delete" prompt="show confirm dialog, delete item, refresh list" />
  <action name="undo" prompt="undo last action" />
  <action name="redo" prompt="redo last undone action" />
</actions>

Guards

Authentication and authorization guards.

xml
<guards>
  <guard name="auth" prompt="if not authenticated, show login dialog" />
  <guard name="unsaved" prompt="if has unsaved changes, show save dialog" />
</guards>

Using Guards

xml
<view name="Editor">
  <guards>
    <guard type="@guard.auth" />
  </guards>
  <!-- View content -->
</view>

FunctionDescription
set view XSwitch to view
open @window.XOpen new window
open @dialog.XOpen modal dialog
close dialogClose current dialog
show context menu @contextMenu.XShow context menu

Example

Complete desktop spec structure:

xml
<?xml version="1.0" encoding="UTF-8"?>
<desktopapp name="MyApp" version="1.0">
  <import src="./myapp.shared.spec.xml" />

  <config>
    <window prompt="title MyApp, width 1200, height 800, min width 800, min height 600" />
    <tray prompt="icon app-icon, tooltip MyApp" />
  </config>

  <guards>
    <guard name="auth" prompt="if not authenticated, show login dialog" />
  </guards>

  <actions>
    <action name="save" prompt="save document, show success toast" />
    <action name="create" prompt="submit form, close dialog, refresh list" />
    <action name="delete" prompt="show confirm dialog, delete item, refresh list" />
  </actions>

  <container type="menu-bar">
    <container type="menu" prompt="label File">
      <element prompt="shortcut Ctrl+N, on click open @dialog.Create">New</element>
      <element prompt="shortcut Ctrl+S, on click @action.save">Save</element>
      <element type="separator" />
      <element prompt="shortcut Ctrl+Comma, on click open @window.Settings">Settings</element>
      <element type="separator" />
      <element prompt="shortcut Alt+F4, on click @action.quit">Exit</element>
    </container>
    <container type="menu" prompt="label Edit">
      <element prompt="shortcut Ctrl+Z, on click @action.undo">Undo</element>
      <element prompt="shortcut Ctrl+Shift+Z, on click @action.redo">Redo</element>
    </container>
  </container>

  <layout name="MainLayout">
    <container type="column" prompt="height 100%">
      <container type="toolbar">
        <element prompt="plus icon, tooltip New, on click open @dialog.Create" />
        <element prompt="save icon, tooltip Save, on click @action.save" />
      </container>

      <container type="split-view" prompt="horizontal, grow">
        <container type="panel" prompt="width 250px, resizable">
          <container type="tree-view">
            <element prompt="home icon, on click @action.setView dashboard">Dashboard</element>
            <container prompt="folder icon, expanded">Items
              <element prompt="on click @action.select with @item.id, context menu @contextMenu.Item" for-each="@state.items" use="@item.name" />
            </container>
          </container>
        </container>
        <container type="panel" prompt="grow">
          <slot name="content" />
        </container>
      </container>

      <container type="status-bar">
        <element>@state.items.length items</element>
        <element type="spacer" />
        <element>Ready</element>
      </container>
    </container>
  </layout>

  <views>
    <view name="Dashboard">
      <guards>
        <guard type="@guard.auth" />
      </guards>
      <container type="scroll" prompt="padding large">
        <element type="text" prompt="heading1">Dashboard</element>
      </container>
    </view>
  </views>

  <dialogs>
    <dialog name="Create" prompt="title New Item, width 450">
      <container type="form" prompt="on submit @action.create, on success close dialog">
        <element type="input" prompt="required, auto focus">Name</element>
        <container type="row" prompt="justify end, gap small">
          <element type="button" prompt="style outline, on click close dialog">Cancel</element>
          <element type="button" prompt="submit, style primary">Create</element>
        </container>
      </container>
    </dialog>
  </dialogs>

  <context-menus>
    <context-menu name="Item">
      <element prompt="edit icon, on click @action.edit with @context.id">Edit</element>
      <element prompt="trash icon, style danger, on click @action.delete with @context.id">Delete</element>
    </context-menu>
  </context-menus>
</desktopapp>

Released under the MIT License.