codex-configurator 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@ npm start
39
39
  - `↑` `↓` : move selection
40
40
  - `PgUp` `PgDn`: move one page up/down
41
41
  - `Home` `End`: jump to first/last item
42
- - `Enter`: open selected table; for mixed scalar/object settings, choose a preset first (object presets open nested settings); for boolean settings, toggle directly; for string settings, open inline input; for other preset values, open picker
42
+ - `Enter`: open selected table; for mixed scalar/object settings, choose a preset first (object presets open nested settings); for boolean settings, toggle directly; for string and numeric scalar settings, open inline input; for other preset values, open picker
43
43
  - `Del`: unset selected value or remove selected custom `<id>` entry from `config.toml`
44
44
  - `←` / `Backspace`: move up one level (to parent table)
45
45
  - `:`: enter command mode
@@ -49,7 +49,7 @@ npm start
49
49
  - `:help`: toggle quick help overlay
50
50
  - `:quit` (or `:q`): quit
51
51
 
52
- The right-hand pane shows what each setting means, plus a picker when a value has preset options.
52
+ The right-hand pane shows what each setting means, the current configured value for plain scalar settings, and a picker when a value has preset options.
53
53
  Deprecated settings are marked with a `[!]` warning marker; only that marker is highlighted.
54
54
  Model picker entries are curated presets maintained by this project.
55
55
  In select lists, `[default]` marks the default option.
package/index.js CHANGED
@@ -20,11 +20,13 @@ import {
20
20
  writeConfig,
21
21
  } from './src/configParser.js';
22
22
  import { getConfigOptions, getConfigVariantMeta } from './src/configHelp.js';
23
- import {
24
- getReferenceOptionForPath,
25
- getReferenceCustomIdPlaceholder,
26
- } from './src/configReference.js';
23
+ import { getReferenceCustomIdPlaceholder } from './src/configReference.js';
27
24
  import { normalizeCustomPathId } from './src/customPathId.js';
25
+ import {
26
+ getReferenceScalarType,
27
+ getScalarEditType,
28
+ parseScalarDraftValue,
29
+ } from './src/scalarEditing.js';
28
30
  import {
29
31
  applyVariantSelection,
30
32
  buildVariantSelectorOptions,
@@ -54,17 +56,6 @@ import { StatusLine } from './src/ui/panes/StatusLine.js';
54
56
  const require = createRequire(import.meta.url);
55
57
  const { version: PACKAGE_VERSION = 'unknown' } = require('./package.json');
56
58
 
57
- const isStringReferenceType = (type) =>
58
- /^string(?:\s|$)/.test(String(type || '').trim());
59
-
60
- const isStringField = (pathSegments, value) => {
61
- if (typeof value === 'string') {
62
- return true;
63
- }
64
-
65
- return isStringReferenceType(getReferenceOptionForPath(pathSegments)?.type);
66
- };
67
-
68
59
  const isCustomIdTableRow = (pathSegments, row) =>
69
60
  row?.kind === 'table' &&
70
61
  typeof row?.pathSegment === 'string' &&
@@ -889,13 +880,14 @@ const App = () => {
889
880
  });
890
881
  };
891
882
 
892
- const beginTextEditing = (target, targetPath) => {
883
+ const beginScalarEditing = (target, targetPath, scalarType) => {
893
884
  setEditError('');
894
885
  setEditMode({
895
- mode: 'text',
886
+ mode: 'scalar',
887
+ scalarType,
896
888
  path: targetPath,
897
- draftValue: typeof target.value === 'string' ? target.value : '',
898
- savedValue: null,
889
+ draftValue:
890
+ typeof target.value === 'undefined' ? '' : String(target.value),
899
891
  });
900
892
  };
901
893
 
@@ -1028,15 +1020,24 @@ const App = () => {
1028
1020
  setEditError('');
1029
1021
  };
1030
1022
 
1031
- const applyTextEdit = () => {
1032
- if (!editMode || editMode.mode !== 'text') {
1023
+ const applyScalarEdit = () => {
1024
+ if (!editMode || editMode.mode !== 'scalar') {
1025
+ return;
1026
+ }
1027
+
1028
+ const parsedValue = parseScalarDraftValue(
1029
+ editMode.draftValue,
1030
+ editMode.scalarType,
1031
+ );
1032
+ if (!parsedValue.ok) {
1033
+ setEditError(parsedValue.error);
1033
1034
  return;
1034
1035
  }
1035
1036
 
1036
1037
  const nextData = setValueAtPath(
1037
1038
  snapshot.ok ? snapshot.data : {},
1038
1039
  editMode.path,
1039
- editMode.draftValue,
1040
+ parsedValue.value,
1040
1041
  );
1041
1042
  if (!ensureAgentConfigFile(nextData, editMode.path)) {
1042
1043
  return;
@@ -1129,17 +1130,13 @@ const App = () => {
1129
1130
  undefined,
1130
1131
  'value',
1131
1132
  ) || [];
1132
- if (requiredOptions.length > 0) {
1133
- return requiredOptions[0];
1134
- }
1133
+ if (requiredOptions.length > 0) {
1134
+ return requiredOptions[0];
1135
+ }
1135
1136
 
1136
- if (
1137
- isStringReferenceType(
1138
- getReferenceOptionForPath(requiredPath)?.type,
1139
- )
1140
- ) {
1141
- return '';
1142
- }
1137
+ if (getReferenceScalarType(requiredPath) === 'string') {
1138
+ return '';
1139
+ }
1143
1140
 
1144
1141
  return {};
1145
1142
  },
@@ -1340,28 +1337,28 @@ const App = () => {
1340
1337
  setCommandInput,
1341
1338
  getCommandInput: () => commandInputRef.current,
1342
1339
  setCommandMessage,
1343
- setEditError,
1344
- beginAddIdEditing,
1345
- beginTextEditing,
1346
- beginVariantEditing,
1347
- beginEditing,
1348
- beginFileSwitchMode,
1349
- applyFileSwitch,
1350
- applyTextEdit,
1351
- applyAddId,
1352
- applyVariantEdit,
1353
- applyEdit,
1340
+ setEditError,
1341
+ beginAddIdEditing,
1342
+ beginScalarEditing,
1343
+ beginVariantEditing,
1344
+ beginEditing,
1345
+ beginFileSwitchMode,
1346
+ applyFileSwitch,
1347
+ applyScalarEdit,
1348
+ applyAddId,
1349
+ applyVariantEdit,
1350
+ applyEdit,
1354
1351
  applyBooleanToggle,
1355
1352
  unsetValueAtPath,
1356
1353
  openPathView,
1357
1354
  reloadActiveConfig,
1358
1355
  getConfigOptions: getConfigOptions,
1359
- getConfigVariantMeta,
1360
- getNodeAtPath,
1361
- buildRows,
1362
- isStringField,
1363
- isCustomIdTableRow,
1364
- resolveMixedVariantBackNavigationPath,
1356
+ getConfigVariantMeta,
1357
+ getNodeAtPath,
1358
+ buildRows,
1359
+ getScalarEditType,
1360
+ isCustomIdTableRow,
1361
+ resolveMixedVariantBackNavigationPath,
1365
1362
  adjustScrollForSelection,
1366
1363
  getSavedIndex,
1367
1364
  readActiveConfigSnapshot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-configurator",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "TOML-aware Ink TUI for Codex Configurator",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -10,6 +10,7 @@ import {
10
10
  import { computePaneWidths, clamp } from '../layout.js';
11
11
  import { getNodeAtPath, buildRows } from '../configParser.js';
12
12
  import { filterRowsByQuery } from '../fuzzySearch.js';
13
+ import { getReadOnlyValuePreviewText } from '../valuePreview.js';
13
14
  import {
14
15
  buildVariantSelectorOptions,
15
16
  isObjectValue,
@@ -112,7 +113,7 @@ const renderArrayDetails = (rows) => {
112
113
  );
113
114
  };
114
115
 
115
- const renderTextEditor = (draftValue) =>
116
+ const renderScalarEditor = (draftValue, scalarType) =>
116
117
  React.createElement(
117
118
  React.Fragment,
118
119
  null,
@@ -124,7 +125,13 @@ const renderTextEditor = (draftValue) =>
124
125
  React.createElement(
125
126
  Text,
126
127
  { color: 'gray', wrap: 'truncate-end' },
127
- 'Type to edit • Enter: save • Esc: cancel',
128
+ `Type ${
129
+ scalarType === 'string'
130
+ ? 'text'
131
+ : scalarType === 'integer'
132
+ ? 'integer'
133
+ : 'number'
134
+ } • Enter: save • Esc: cancel`,
128
135
  ),
129
136
  );
130
137
 
@@ -416,6 +423,10 @@ export const ConfigNavigator = ({
416
423
  );
417
424
  const shouldShowReadOnlyOptions =
418
425
  readOnlyOptions.length > 0 && !isBooleanOnlyOptions(readOnlyOptions);
426
+ const readOnlyValuePreviewText = getReadOnlyValuePreviewText(
427
+ selectedRow,
428
+ shouldShowReadOnlyOptions,
429
+ );
419
430
 
420
431
  const editRow = rows[selected] || null;
421
432
  const editDefaultOption =
@@ -471,8 +482,8 @@ export const ConfigNavigator = ({
471
482
  )
472
483
  : null;
473
484
  const optionSelector = editMode
474
- ? editMode.mode === 'text'
475
- ? renderTextEditor(editMode.draftValue)
485
+ ? editMode.mode === 'scalar'
486
+ ? renderScalarEditor(editMode.draftValue, editMode.scalarType)
476
487
  : editMode.mode === 'add-id'
477
488
  ? renderIdEditor(editMode.placeholder, editMode.draftValue)
478
489
  : renderEditableOptions(
@@ -497,6 +508,17 @@ export const ConfigNavigator = ({
497
508
  false,
498
509
  ),
499
510
  )
511
+ : readOnlyValuePreviewText
512
+ ? React.createElement(
513
+ React.Fragment,
514
+ null,
515
+ React.createElement(Text, { color: 'gray' }, ' '),
516
+ React.createElement(
517
+ Text,
518
+ { color: 'white', wrap: 'truncate-end' },
519
+ readOnlyValuePreviewText,
520
+ ),
521
+ )
500
522
  : selectedRow?.kind === 'array'
501
523
  ? renderArrayDetails(selectedRow.value)
502
524
  : null;
package/src/constants.js CHANGED
@@ -3,7 +3,7 @@ export const CONTROL_HINT =
3
3
  export const FILE_SWITCH_HINT =
4
4
  '↑/↓ choose • PgUp/PgDn page • Home/End jump • Enter switch file • Esc/Backspace/← cancel';
5
5
  export const EDIT_CONTROL_HINT =
6
- '↑/↓ choose • PgUp/PgDn page • Home/End jump • Enter save • Esc/Backspace/← cancel • Del delete char (text input)';
6
+ '↑/↓ choose • PgUp/PgDn page • Home/End jump • Enter save • Esc/Backspace/← cancel • Del delete char (inline input)';
7
7
  export const FILTER_CONTROL_HINT =
8
8
  'Type filter • Enter/Esc: done • Del/Backspace: delete • Ctrl+U: clear';
9
9
  export const COMMAND_HINT = 'Type : to see commands';
@@ -20,6 +20,13 @@
20
20
  "description": {
21
21
  "description": "Human-facing role documentation used in spawn tool guidance.",
22
22
  "type": "string"
23
+ },
24
+ "nickname_candidates": {
25
+ "description": "Candidate nicknames for agents spawned with this role.",
26
+ "items": {
27
+ "type": "string"
28
+ },
29
+ "type": "array"
23
30
  }
24
31
  },
25
32
  "type": "object"
@@ -313,6 +320,9 @@
313
320
  "apps_mcp_gateway": {
314
321
  "type": "boolean"
315
322
  },
323
+ "artifact": {
324
+ "type": "boolean"
325
+ },
316
326
  "child_agents_md": {
317
327
  "type": "boolean"
318
328
  },
@@ -349,6 +359,15 @@
349
359
  "experimental_windows_sandbox": {
350
360
  "type": "boolean"
351
361
  },
362
+ "fast_mode": {
363
+ "type": "boolean"
364
+ },
365
+ "image_detail_original": {
366
+ "type": "boolean"
367
+ },
368
+ "image_generation": {
369
+ "type": "boolean"
370
+ },
352
371
  "include_apply_patch_tool": {
353
372
  "type": "boolean"
354
373
  },
@@ -370,6 +389,9 @@
370
389
  "personality": {
371
390
  "type": "boolean"
372
391
  },
392
+ "plugins": {
393
+ "type": "boolean"
394
+ },
373
395
  "powershell_utf8": {
374
396
  "type": "boolean"
375
397
  },
@@ -502,6 +524,9 @@
502
524
  "sandbox_mode": {
503
525
  "$ref": "#/definitions/SandboxMode"
504
526
  },
527
+ "service_tier": {
528
+ "$ref": "#/definitions/ServiceTier"
529
+ },
505
530
  "tools_view_image": {
506
531
  "type": "boolean"
507
532
  },
@@ -613,7 +638,19 @@
613
638
  "additionalProperties": false,
614
639
  "description": "Memories settings loaded from config.toml.",
615
640
  "properties": {
616
- "max_raw_memories_for_global": {
641
+ "consolidation_model": {
642
+ "description": "Model used for memory consolidation.",
643
+ "type": "string"
644
+ },
645
+ "extract_model": {
646
+ "description": "Model used for thread summarisation.",
647
+ "type": "string"
648
+ },
649
+ "generate_memories": {
650
+ "description": "When `false`, newly created threads are stored with `memory_mode = \"disabled\"` in the state DB.",
651
+ "type": "boolean"
652
+ },
653
+ "max_raw_memories_for_consolidation": {
617
654
  "description": "Maximum number of recent raw memories retained for global consolidation.",
618
655
  "format": "uint",
619
656
  "minimum": 0,
@@ -640,17 +677,25 @@
640
677
  "format": "int64",
641
678
  "type": "integer"
642
679
  },
643
- "phase_1_model": {
644
- "description": "Model used for thread summarisation.",
645
- "type": "string"
680
+ "no_memories_if_mcp_or_web_search": {
681
+ "description": "When `true`, web searches and MCP tool calls mark the thread `memory_mode` as `\"polluted\"`.",
682
+ "type": "boolean"
646
683
  },
647
- "phase_2_model": {
648
- "description": "Model used for memory consolidation.",
649
- "type": "string"
684
+ "use_memories": {
685
+ "description": "When `false`, skip injecting memory usage instructions into developer prompts.",
686
+ "type": "boolean"
650
687
  }
651
688
  },
652
689
  "type": "object"
653
690
  },
691
+ "ModelAvailabilityNuxConfig": {
692
+ "additionalProperties": {
693
+ "format": "uint32",
694
+ "minimum": 0,
695
+ "type": "integer"
696
+ },
697
+ "type": "object"
698
+ },
654
699
  "ModelProviderInfo": {
655
700
  "additionalProperties": false,
656
701
  "description": "Serializable representation of a provider definition.",
@@ -1069,6 +1114,16 @@
1069
1114
  ],
1070
1115
  "type": "string"
1071
1116
  },
1117
+ "PluginConfig": {
1118
+ "additionalProperties": false,
1119
+ "properties": {
1120
+ "enabled": {
1121
+ "default": true,
1122
+ "type": "boolean"
1123
+ }
1124
+ },
1125
+ "type": "object"
1126
+ },
1072
1127
  "ProjectConfig": {
1073
1128
  "additionalProperties": false,
1074
1129
  "properties": {
@@ -1146,6 +1201,10 @@
1146
1201
  },
1147
1202
  "type": "object"
1148
1203
  },
1204
+ "oauth_resource": {
1205
+ "default": null,
1206
+ "type": "string"
1207
+ },
1149
1208
  "required": {
1150
1209
  "default": null,
1151
1210
  "type": "boolean"
@@ -1179,6 +1238,18 @@
1179
1238
  },
1180
1239
  "type": "object"
1181
1240
  },
1241
+ "RealtimeAudioToml": {
1242
+ "additionalProperties": false,
1243
+ "properties": {
1244
+ "microphone": {
1245
+ "type": "string"
1246
+ },
1247
+ "speaker": {
1248
+ "type": "string"
1249
+ }
1250
+ },
1251
+ "type": "object"
1252
+ },
1182
1253
  "ReasoningEffort": {
1183
1254
  "description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
1184
1255
  "enum": [
@@ -1266,6 +1337,13 @@
1266
1337
  },
1267
1338
  "type": "object"
1268
1339
  },
1340
+ "ServiceTier": {
1341
+ "enum": [
1342
+ "fast",
1343
+ "flex"
1344
+ ],
1345
+ "type": "string"
1346
+ },
1269
1347
  "ShellEnvironmentPolicyInherit": {
1270
1348
  "oneOf": [
1271
1349
  {
@@ -1396,6 +1474,15 @@
1396
1474
  "description": "Enable animations (welcome screen, shimmer effects, spinners). Defaults to `true`.",
1397
1475
  "type": "boolean"
1398
1476
  },
1477
+ "model_availability_nux": {
1478
+ "allOf": [
1479
+ {
1480
+ "$ref": "#/definitions/ModelAvailabilityNuxConfig"
1481
+ }
1482
+ ],
1483
+ "default": {},
1484
+ "description": "Startup tooltip availability NUX state persisted by the TUI."
1485
+ },
1399
1486
  "notification_method": {
1400
1487
  "allOf": [
1401
1488
  {
@@ -1540,6 +1627,15 @@
1540
1627
  "default": null,
1541
1628
  "description": "Settings for app-specific controls."
1542
1629
  },
1630
+ "audio": {
1631
+ "allOf": [
1632
+ {
1633
+ "$ref": "#/definitions/RealtimeAudioToml"
1634
+ }
1635
+ ],
1636
+ "default": null,
1637
+ "description": "Machine-local realtime audio device preferences used by realtime voice."
1638
+ },
1543
1639
  "background_terminal_max_timeout": {
1544
1640
  "description": "Maximum poll window for background terminal output (`write_stdin`), in milliseconds. Default: `300000` (5 minutes).",
1545
1641
  "format": "uint64",
@@ -1584,11 +1680,15 @@
1584
1680
  "$ref": "#/definitions/AbsolutePathBuf"
1585
1681
  },
1586
1682
  "experimental_realtime_ws_backend_prompt": {
1587
- "description": "Experimental / do not use. Overrides only the realtime conversation websocket transport backend prompt (the `Op::RealtimeConversation` `/ws` session.create backend_prompt) without changing normal prompts.",
1683
+ "description": "Experimental / do not use. Overrides only the realtime conversation websocket transport instructions (the `Op::RealtimeConversation` `/ws` session.update instructions) without changing normal prompts.",
1588
1684
  "type": "string"
1589
1685
  },
1590
1686
  "experimental_realtime_ws_base_url": {
1591
- "description": "Experimental / do not use. Overrides only the realtime conversation websocket transport base URL (the `Op::RealtimeConversation` `/ws` connection) without changing normal provider HTTP requests.",
1687
+ "description": "Experimental / do not use. Overrides only the realtime conversation websocket transport base URL (the `Op::RealtimeConversation` `/v1/realtime` connection) without changing normal provider HTTP requests.",
1688
+ "type": "string"
1689
+ },
1690
+ "experimental_realtime_ws_model": {
1691
+ "description": "Experimental / do not use. Selects the realtime websocket model/snapshot used for the `Op::RealtimeConversation` connection.",
1592
1692
  "type": "string"
1593
1693
  },
1594
1694
  "experimental_use_freeform_apply_patch": {
@@ -1611,6 +1711,9 @@
1611
1711
  "apps_mcp_gateway": {
1612
1712
  "type": "boolean"
1613
1713
  },
1714
+ "artifact": {
1715
+ "type": "boolean"
1716
+ },
1614
1717
  "child_agents_md": {
1615
1718
  "type": "boolean"
1616
1719
  },
@@ -1647,6 +1750,15 @@
1647
1750
  "experimental_windows_sandbox": {
1648
1751
  "type": "boolean"
1649
1752
  },
1753
+ "fast_mode": {
1754
+ "type": "boolean"
1755
+ },
1756
+ "image_detail_original": {
1757
+ "type": "boolean"
1758
+ },
1759
+ "image_generation": {
1760
+ "type": "boolean"
1761
+ },
1650
1762
  "include_apply_patch_tool": {
1651
1763
  "type": "boolean"
1652
1764
  },
@@ -1668,6 +1780,9 @@
1668
1780
  "personality": {
1669
1781
  "type": "boolean"
1670
1782
  },
1783
+ "plugins": {
1784
+ "type": "boolean"
1785
+ },
1671
1786
  "powershell_utf8": {
1672
1787
  "type": "boolean"
1673
1788
  },
@@ -1965,6 +2080,14 @@
1965
2080
  "plan_mode_reasoning_effort": {
1966
2081
  "$ref": "#/definitions/ReasoningEffort"
1967
2082
  },
2083
+ "plugins": {
2084
+ "additionalProperties": {
2085
+ "$ref": "#/definitions/PluginConfig"
2086
+ },
2087
+ "default": {},
2088
+ "description": "User-level plugin config entries keyed by plugin name.",
2089
+ "type": "object"
2090
+ },
1968
2091
  "profile": {
1969
2092
  "description": "Profile to use from the `profiles` map.",
1970
2093
  "type": "string"
@@ -2024,6 +2147,14 @@
2024
2147
  ],
2025
2148
  "description": "Sandbox configuration to apply if `sandbox` is `WorkspaceWrite`."
2026
2149
  },
2150
+ "service_tier": {
2151
+ "allOf": [
2152
+ {
2153
+ "$ref": "#/definitions/ServiceTier"
2154
+ }
2155
+ ],
2156
+ "description": "Optional explicit service tier preference for new turns (`fast` or `flex`)."
2157
+ },
2027
2158
  "shell_environment_policy": {
2028
2159
  "allOf": [
2029
2160
  {
@@ -0,0 +1,117 @@
1
+ import { getReferenceOptionForPath } from './configReference.js';
2
+
3
+ const STRING_REFERENCE_TYPE_PATTERN = /^string(?:\s|$)/;
4
+ const DIGIT_GROUP_PATTERN = '\\d(?:_?\\d)*';
5
+ const INTEGER_DRAFT_PATTERN = new RegExp(
6
+ `^[+-]?${DIGIT_GROUP_PATTERN}$`,
7
+ );
8
+ const NUMBER_DRAFT_PATTERN = new RegExp(
9
+ `^[+-]?(?:(?:${DIGIT_GROUP_PATTERN}(?:\\.(?:${DIGIT_GROUP_PATTERN})?)?)|(?:\\.${DIGIT_GROUP_PATTERN}))(?:[eE][+-]?${DIGIT_GROUP_PATTERN})?$`,
10
+ );
11
+
12
+ export const getReferenceScalarType = (pathSegments) => {
13
+ const referenceType = String(
14
+ getReferenceOptionForPath(pathSegments)?.type || '',
15
+ ).trim();
16
+
17
+ if (STRING_REFERENCE_TYPE_PATTERN.test(referenceType)) {
18
+ return 'string';
19
+ }
20
+
21
+ if (referenceType === 'integer') {
22
+ return 'integer';
23
+ }
24
+
25
+ if (referenceType === 'number') {
26
+ return 'number';
27
+ }
28
+
29
+ return null;
30
+ };
31
+
32
+ export const getScalarEditType = (pathSegments, value) => {
33
+ const referenceScalarType = getReferenceScalarType(pathSegments);
34
+ if (referenceScalarType) {
35
+ return referenceScalarType;
36
+ }
37
+
38
+ if (typeof value === 'string') {
39
+ return 'string';
40
+ }
41
+
42
+ if (typeof value === 'number') {
43
+ return Number.isInteger(value) ? 'integer' : 'number';
44
+ }
45
+ return null;
46
+ };
47
+
48
+ export const parseScalarDraftValue = (draftValue, scalarType) => {
49
+ const nextDraftValue =
50
+ typeof draftValue === 'string'
51
+ ? draftValue
52
+ : String(draftValue ?? '');
53
+
54
+ if (scalarType === 'string') {
55
+ return {
56
+ ok: true,
57
+ value: nextDraftValue,
58
+ };
59
+ }
60
+
61
+ const normalized = nextDraftValue.trim();
62
+ if (!normalized) {
63
+ return {
64
+ ok: false,
65
+ error: 'Value cannot be empty. Use Del to unset it.',
66
+ };
67
+ }
68
+
69
+ if (scalarType === 'integer') {
70
+ if (!INTEGER_DRAFT_PATTERN.test(normalized)) {
71
+ return {
72
+ ok: false,
73
+ error: 'Value must be a whole number.',
74
+ };
75
+ }
76
+
77
+ const parsed = Number(normalized.replaceAll('_', ''));
78
+ if (!Number.isSafeInteger(parsed)) {
79
+ return {
80
+ ok: false,
81
+ error: 'Value must be a safe integer.',
82
+ };
83
+ }
84
+
85
+ return {
86
+ ok: true,
87
+ value: parsed,
88
+ };
89
+ }
90
+
91
+ if (scalarType === 'number') {
92
+ if (!NUMBER_DRAFT_PATTERN.test(normalized)) {
93
+ return {
94
+ ok: false,
95
+ error: 'Value must be a finite decimal number.',
96
+ };
97
+ }
98
+
99
+ const parsed = Number(normalized.replaceAll('_', ''));
100
+ if (!Number.isFinite(parsed)) {
101
+ return {
102
+ ok: false,
103
+ error: 'Value must be a finite decimal number.',
104
+ };
105
+ }
106
+
107
+ return {
108
+ ok: true,
109
+ value: parsed,
110
+ };
111
+ }
112
+
113
+ return {
114
+ ok: false,
115
+ error: 'Unsupported scalar type.',
116
+ };
117
+ };
@@ -15,7 +15,7 @@ import {
15
15
  FILTER_CONTROL_HINT,
16
16
  } from '../constants.js';
17
17
 
18
- const isInlineTextMode = (mode) => mode === 'text' || mode === 'add-id';
18
+ const isInlineDraftMode = (mode) => mode === 'scalar' || mode === 'add-id';
19
19
 
20
20
  const isBooleanOnlyOptions = (options) =>
21
21
  Array.isArray(options) &&
@@ -215,7 +215,7 @@ export const executeInputCommand = ({ input, key, context }) => {
215
215
  configFileCatalog,
216
216
  } = context;
217
217
 
218
- const isTextEditing = isInlineTextMode(editMode?.mode);
218
+ const isInlineEditing = isInlineDraftMode(editMode?.mode);
219
219
  const isCapturingCommand = isCommandModeLocked || isCommandMode;
220
220
 
221
221
  if (isCapturingCommand) {
@@ -382,10 +382,10 @@ export const executeInputCommand = ({ input, key, context }) => {
382
382
  }
383
383
 
384
384
  if (editMode) {
385
- if (isTextEditing) {
385
+ if (isInlineEditing) {
386
386
  if (key.return) {
387
- if (editMode.mode === 'text') {
388
- context.applyTextEdit();
387
+ if (editMode.mode === 'scalar') {
388
+ context.applyScalarEdit();
389
389
  return true;
390
390
  }
391
391
 
@@ -669,8 +669,12 @@ export const executeInputCommand = ({ input, key, context }) => {
669
669
  return true;
670
670
  }
671
671
 
672
- if (context.isStringField(targetPath, target.value)) {
673
- context.beginTextEditing(target, targetPath);
672
+ const scalarType = context.getScalarEditType(
673
+ targetPath,
674
+ target.value,
675
+ );
676
+ if (scalarType) {
677
+ context.beginScalarEditing(target, targetPath, scalarType);
674
678
  }
675
679
 
676
680
  return true;
@@ -0,0 +1,38 @@
1
+ const isPrimitivePreviewValue = (value) =>
2
+ value === null ||
3
+ typeof value === 'string' ||
4
+ typeof value === 'number' ||
5
+ typeof value === 'boolean';
6
+
7
+ export const formatReadOnlyValuePreview = (value) => {
8
+ if (typeof value === 'string') {
9
+ return JSON.stringify(value);
10
+ }
11
+
12
+ if (
13
+ typeof value === 'number' ||
14
+ typeof value === 'boolean' ||
15
+ value === null
16
+ ) {
17
+ return String(value);
18
+ }
19
+
20
+ return String(value);
21
+ };
22
+
23
+ export const getReadOnlyValuePreviewText = (
24
+ row,
25
+ shouldShowReadOnlyOptions = false,
26
+ ) => {
27
+ if (
28
+ !row ||
29
+ row.kind !== 'value' ||
30
+ row.isConfigured !== true ||
31
+ shouldShowReadOnlyOptions ||
32
+ !isPrimitivePreviewValue(row.value)
33
+ ) {
34
+ return null;
35
+ }
36
+
37
+ return `Current: ${formatReadOnlyValuePreview(row.value)}`;
38
+ };