When Clauses

When clauses gate visibility and executability of docks, commands, and custom UI via VS Code's when-clause contexts. The evaluator whenexpr re-exports at devframe/utils/when.

When clauses gate visibility and executability of docks, commands, and custom UI via VS Code's when-clause contexts. The evaluator whenexpr re-exports at devframe/utils/when.

Usage

On commands

Gates palette visibility and shortcuts.

ctx.commands.register({
  id: 'my-devtool:embedded-only',
  title: 'Embedded-Only Action',
  when: 'clientType == embedded',
  handler: async () => { /* … */ },
})

On dock entries

Gates dock-rail visibility.

ctx.docks.register({
  id: 'my-devtool:inspector',
  title: 'Inspector',
  type: 'action',
  icon: 'ph:cursor-duotone',
  when: 'clientType == embedded',
  action: { importFrom: 'my-devtool/inspector' },
})

Render-only visibility on dock entries

A dock entry also takes visibility, a second expression that hides only its dock-rail button while keeping the entry registered and reachable (e.g. a subTabs anchor).

ctx.docks.register({
  id: 'my-devtool:anchor',
  title: 'My Devtool',
  type: 'iframe',
  icon: 'ph:squares-four-duotone',
  url: '/__my-devtool/',
  subTabs: { protocol: 'postmessage' },
  visibility: 'false', // hide the anchor's own button; its tabs still render
})

Expression syntax

Operators

CategoryOperatorsExample
Bare truthyidentifierdockOpen
Literalstrue, false, numbers, strings42, 'dev'
Unary!, -, +!paletteOpen
Logical&&, ||dockOpen && !paletteOpen
Equality==, !=, ===, !==clientType == embedded
Relational<, <=, >, >=count >= 10
Arithmetic+, -, *, /, %(a + b) * c
Grouping( … )(a || b)

Precedence (low → high)

||&& → equality → relational → + -* / % → unary → primary.

== vs ===

  • == / != — VS Code idiom; RHS a single token, compared as a string.
  • === / !== — JS strict equality; full expressions both sides, no coercion.
evaluateWhen('clientType == embedded', ctx) // string-style
evaluateWhen('count === 1', { count: 1 }) // true
evaluateWhen('count === 1', { count: '1' }) // false

Examples

when: 'true' // always visible
when: 'false' // never visible
when: 'clientType == embedded' // only embedded
when: 'dockOpen && !paletteOpen' // dock open and palette closed
when: '(clientType == embedded && dockOpen) || clientType == standalone'
when: 'my-devtool.ready' // custom devframe context

Built-in context variables

VariableTypeDescription
clientType'embedded' | 'standalone'embedded in the host page's overlay, standalone in a separate window.
dockOpenbooleanDock panel open.
paletteOpenbooleanCommand palette open.
dockSelectedIdstringSelected dock entry ID; '' if none.

Namespaced context keys

Devframes add keys with . or ::

context['my-devtool.ready'] = true
context['my-devtool:step'] = 'build'
context.myDevtool = { ready: true, step: 'build' } // nested form
when: 'my-devtool.ready'
when: 'my-devtool:step == build'
when: 'myDevtool.ready'

Lookup order

Resolves the exact key ctx['my-devtool.ready'] first, then nested ctx['my-devtool']?.ready; flat wins.

Type-safe when clauses

defineCommand and defineDockEntry type-check when: against WhenContext.

import { defineCommand } from 'devframe'

defineCommand({
  id: 'my-devtool:toggle',
  title: 'Toggle',
  when: 'dockOpen && !paletteOpen', // ✓ ok
  handler: async () => {},
})

defineCommand({
  id: 'my-devtool:broken',
  title: 'Broken',
  when: 'dockOpen &&& !paletteOpen',
  //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Type error: syntax error
  handler: async () => {},
})

Key validation with devframe contexts

The default WhenContext leaves devframe keys open-ended ([key: string]: unknown). To validate names, declare a narrower context and wrapper:

import type { WhenContext, WhenExpression } from 'devframe/utils/when'

interface MyPluginContext extends Omit<WhenContext, keyof any> {
  'clientType': 'embedded' | 'standalone'
  'dockOpen': boolean
  'paletteOpen': boolean
  'dockSelectedId': string
  'my-devtool.ready': boolean
}

function defineMyCommand<const W extends string>(cmd: {
  id: string
  title: string
  when?: WhenExpression<MyPluginContext, W>
  handler: (...args: any[]) => Promise<unknown>
}): typeof cmd {
  return cmd
}

defineMyCommand({
  id: 'my-devtool:toggle',
  title: 'Toggle',
  when: 'my-devtool.ready && dockOpen', // ✓ ok
  handler: async () => {},
})

defineMyCommand({
  id: 'my-devtool:broken',
  title: 'Broken',
  when: 'my-devtool.read', // ← typo
  //    ^^^^^^^^^^^^^^^^^^^ Type error: Unknown context key
  handler: async () => {},
})

API reference

import type { WhenContext } from 'devframe/utils/when'
import { evaluateWhen, resolveContextValue } from 'devframe/utils/when'

const ctx: WhenContext = {
  'clientType': 'embedded',
  'dockOpen': true,
  'paletteOpen': false,
  'dockSelectedId': 'my-dock',
  'my-devtool.ready': true,
}

evaluateWhen('dockOpen && my-devtool.ready', ctx) // true
evaluateWhen('clientType == standalone', ctx) // false

resolveContextValue('my-devtool.ready', ctx) // true

evaluateWhen(expression, ctx, options?)

Returns boolean; { strict: true } throws on unknown keys.

resolveContextValue(key, ctx)

Returns one (possibly namespaced) key's value.

WhenExpression<Ctx, S>

The branded whenexpr expression type for typed define* helpers (above).