showSettingsPanel function

void showSettingsPanel(
  1. AppContext ctx
)

Opens the global settings panel — a modal where the user picks the terminal dimensions for new sessions and the terminal text size. Both choices persist immediately (via TerminalDisplayController); dimension changes take effect the next time a session is opened, while text size applies live.

Implementation

void showSettingsPanel(AppContext ctx) {
  final display = ctx.display;
  final device = deviceMetrics();
  final landscape = computeFitDims(device.landscape);
  final portrait = computeFitDims(device.portrait);

  // Custom cols/rows inputs (revealed when the "Custom" preset is selected).
  final cols = input(
    id: 'term-cols',
    type: 'number',
    value: '${display.customCols.value}',
  );
  final rows = input(
    id: 'term-rows',
    type: 'number',
    value: '${display.customRows.value}',
  );
  void commitCustom() {
    final c = int.tryParse(cols.value) ?? display.customCols.value;
    final r = int.tryParse(rows.value) ?? display.customRows.value;
    display.setCustom(c, r);
    // Reflect any clamping back into the inputs.
    cols.value = '${display.customCols.value}';
    rows.value = '${display.customRows.value}';
  }

  on(cols, 'change', (_) => commitCustom());
  on(rows, 'change', (_) => commitCustom());

  final customFields = el(
    'div',
    classes: 'row custom-dims',
    children: [
      field('Columns', cols, hint: '$kMinCols–$kMaxCols'),
      field('Rows', rows, hint: '$kMinRows–$kMaxRows'),
    ],
  );
  void syncCustomEnabled() {
    final enabled = display.preset.value == DimensionPreset.custom;
    customFields.classList.toggle('disabled', !enabled);
    cols.disabled = !enabled;
    rows.disabled = !enabled;
  }

  final dimensions = radioGroup(
    name: 'term-dim',
    ariaLabel: 'Terminal size',
    selected: display.preset.value.name,
    options: [
      (value: DimensionPreset.autoFit.name, label: 'Auto-fit (current screen)'),
      (
        value: DimensionPreset.landscape.name,
        label: 'Fit landscape · ${landscape.cols}×${landscape.rows}',
      ),
      (
        value: DimensionPreset.portrait.name,
        label: 'Fit portrait · ${portrait.cols}×${portrait.rows}',
      ),
      (value: DimensionPreset.standard.name, label: 'Standard · 80×24'),
      (value: DimensionPreset.custom.name, label: 'Custom'),
    ],
    onChange: (value) {
      display.setPreset(DimensionPreset.parse(value));
      syncCustomEnabled();
    },
  );
  syncCustomEnabled();

  final textSize = radioGroup(
    name: 'term-text',
    ariaLabel: 'Text size',
    selected: display.textSize.value.name,
    inline: true,
    options: [
      (value: TerminalTextSize.auto.name, label: 'Auto'),
      (value: TerminalTextSize.smaller.name, label: 'Smaller'),
      (value: TerminalTextSize.normal.name, label: 'Normal'),
      (value: TerminalTextSize.larger.name, label: 'Larger'),
    ],
    onChange: (value) => display.setTextSize(TerminalTextSize.parse(value)),
  );

  // The AI-agent controls are the portable half — see [aiSettingsSection].
  late final Modal modal;
  final body = el(
    'div',
    classes: 'stack settings-panel',
    children: [
      el('h3', text: 'Terminal size'),
      dimensions,
      customFields,
      el(
        'div',
        classes: 'hint',
        text:
            'Applies to the next session you open — '
            'running sessions keep their size.',
      ),
      el('h3', text: 'Text size'),
      textSize,
      el('div', classes: 'hint', text: 'Applies immediately.'),
      el('hr'),
      ...aiSettingsSection(ctx.ai),
    ],
  );

  modal = Modal(
    title: 'Settings',
    body: body,
    actions: [button('Close', primary: true, onClick: () => modal.close())],
  );
  modal.show();
}