codex-configurator 0.2.5 → 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 +15 -3
- package/index.js +119 -53
- package/package.json +1 -1
- package/src/appState.js +11 -0
- package/src/components/ConfigNavigator.js +26 -4
- package/src/components/Header.js +0 -5
- package/src/configFeatures.js +15 -16
- package/src/configReference.js +66 -19
- package/src/constants.js +1 -1
- package/src/reference/config-schema.json +140 -9
- package/src/scalarEditing.js +117 -0
- package/src/schemaRuntimeSync.js +282 -0
- package/src/ui/commands.js +11 -7
- package/src/ui/panes/StatusLine.js +71 -28
- package/src/valuePreview.js +38 -0
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,
|
|
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.
|
|
@@ -120,6 +120,8 @@ You can override either command with:
|
|
|
120
120
|
- `CODEX_CONFIGURATOR_CODEX_BIN=<command-or-path>`
|
|
121
121
|
- `CODEX_CONFIGURATOR_NPM_BIN=<command-or-path>`
|
|
122
122
|
|
|
123
|
+
The status bar shows `Using Codex v<version>` only after Codex version detection completes. During startup schema sync it shows `Checking schema`, and after that it shows `Checking Codex version` until the Codex check finishes. When needed, it shows Codex update state as `Update available` or `Version check unavailable`; if there is no warning badge, assume Codex is up to date. The schema-progress state is kept visible for at least `1.2s` so it is observable even on very fast connections. The Codex version check starts only after schema sync completes.
|
|
124
|
+
|
|
123
125
|
## Self-update behavior
|
|
124
126
|
|
|
125
127
|
On startup, the app checks the npm registry for the latest `codex-configurator` version.
|
|
@@ -132,7 +134,17 @@ This uses the `npm` command from `PATH` (or `CODEX_CONFIGURATOR_NPM_BIN` if set)
|
|
|
132
134
|
## Upstream reference
|
|
133
135
|
|
|
134
136
|
- Canonical config schema: https://developers.openai.com/codex/config-schema.json
|
|
135
|
-
-
|
|
137
|
+
- Runtime behavior:
|
|
138
|
+
- App starts with bundled fallback schema from `src/reference/config-schema.json`.
|
|
139
|
+
- App then checks for updates on startup in the background.
|
|
140
|
+
- If a valid cached schema exists, it is loaded immediately.
|
|
141
|
+
- If upstream has a newer valid schema, it is applied immediately and cached for future launches.
|
|
142
|
+
- If the check fails, the app silently continues with the already-active schema.
|
|
143
|
+
- Runtime cache location: `<codex-dir>/codex-configurator-cache/`
|
|
144
|
+
- Default codex dir: `~/.codex`
|
|
145
|
+
- Cached files:
|
|
146
|
+
- `config-schema.json`
|
|
147
|
+
- `config-schema.meta.json`
|
|
136
148
|
- To refresh the reference snapshot after a new stable Codex release:
|
|
137
149
|
- `npm run sync:reference`
|
|
138
150
|
- Snapshot currently synced against Codex stable reference updates published on 2026-02-25.
|
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,
|
|
@@ -36,6 +38,7 @@ import {
|
|
|
36
38
|
import {
|
|
37
39
|
APP_MODES,
|
|
38
40
|
APP_STATE_ACTION,
|
|
41
|
+
REFERENCE_SCHEMA_CHANGED_ACTION,
|
|
39
42
|
appStateReducer,
|
|
40
43
|
buildInitialAppState,
|
|
41
44
|
} from './src/appState.js';
|
|
@@ -43,6 +46,7 @@ import { pathToKey, clamp, computeListViewportRows } from './src/layout.js';
|
|
|
43
46
|
import { Header } from './src/components/Header.js';
|
|
44
47
|
import { ConfigNavigator } from './src/components/ConfigNavigator.js';
|
|
45
48
|
import { filterRowsByQuery } from './src/fuzzySearch.js';
|
|
49
|
+
import { syncReferenceSchemaAtStartup } from './src/schemaRuntimeSync.js';
|
|
46
50
|
import { executeInputCommand, getModeHint } from './src/ui/commands.js';
|
|
47
51
|
import { CommandBar } from './src/ui/panes/CommandBar.js';
|
|
48
52
|
import { HelpBubble } from './src/ui/panes/HelpBubble.js';
|
|
@@ -52,17 +56,6 @@ import { StatusLine } from './src/ui/panes/StatusLine.js';
|
|
|
52
56
|
const require = createRequire(import.meta.url);
|
|
53
57
|
const { version: PACKAGE_VERSION = 'unknown' } = require('./package.json');
|
|
54
58
|
|
|
55
|
-
const isStringReferenceType = (type) =>
|
|
56
|
-
/^string(?:\s|$)/.test(String(type || '').trim());
|
|
57
|
-
|
|
58
|
-
const isStringField = (pathSegments, value) => {
|
|
59
|
-
if (typeof value === 'string') {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return isStringReferenceType(getReferenceOptionForPath(pathSegments)?.type);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
59
|
const isCustomIdTableRow = (pathSegments, row) =>
|
|
67
60
|
row?.kind === 'table' &&
|
|
68
61
|
typeof row?.pathSegment === 'string' &&
|
|
@@ -75,6 +68,7 @@ const UPDATE_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
|
75
68
|
const CODEX_BIN_ENV_VAR = 'CODEX_CONFIGURATOR_CODEX_BIN';
|
|
76
69
|
const NPM_BIN_ENV_VAR = 'CODEX_CONFIGURATOR_NPM_BIN';
|
|
77
70
|
const CONFIGURATOR_PACKAGE_NAME = 'codex-configurator';
|
|
71
|
+
const SCHEMA_STATUS_MIN_VISIBLE_MS = 1200;
|
|
78
72
|
const FILE_SWITCH_MAX_VISIBLE_ENTRIES = 6;
|
|
79
73
|
const FILE_SWITCH_PANEL_BASE_ROWS = 3;
|
|
80
74
|
const FILE_SWITCH_LAYOUT_EXTRA_GAP_ROWS = 1;
|
|
@@ -174,6 +168,11 @@ const runCommand = async (command, args = [], options = {}) => {
|
|
|
174
168
|
return result.stdout;
|
|
175
169
|
};
|
|
176
170
|
|
|
171
|
+
const waitForMilliseconds = (milliseconds) =>
|
|
172
|
+
new Promise((resolve) => {
|
|
173
|
+
setTimeout(resolve, Math.max(0, Number(milliseconds) || 0));
|
|
174
|
+
});
|
|
175
|
+
|
|
177
176
|
const getConfiguredCommand = (environmentVariableName, fallbackCommand) => {
|
|
178
177
|
const configuredCommand = String(
|
|
179
178
|
process.env[environmentVariableName] || '',
|
|
@@ -364,7 +363,7 @@ const getCodexUpdateStatus = async () => {
|
|
|
364
363
|
return {
|
|
365
364
|
installed: installedLabel,
|
|
366
365
|
latest: 'unknown',
|
|
367
|
-
status: '
|
|
366
|
+
status: 'Version check unavailable',
|
|
368
367
|
};
|
|
369
368
|
}
|
|
370
369
|
|
|
@@ -380,7 +379,7 @@ const getCodexUpdateStatus = async () => {
|
|
|
380
379
|
return {
|
|
381
380
|
installed,
|
|
382
381
|
latest: 'unknown',
|
|
383
|
-
status: '
|
|
382
|
+
status: 'Version check unavailable',
|
|
384
383
|
};
|
|
385
384
|
}
|
|
386
385
|
|
|
@@ -389,14 +388,14 @@ const getCodexUpdateStatus = async () => {
|
|
|
389
388
|
return {
|
|
390
389
|
installed,
|
|
391
390
|
latest,
|
|
392
|
-
status:
|
|
391
|
+
status: 'Update available',
|
|
393
392
|
};
|
|
394
393
|
}
|
|
395
394
|
|
|
396
395
|
return {
|
|
397
396
|
installed,
|
|
398
397
|
latest,
|
|
399
|
-
status: '
|
|
398
|
+
status: 'Up to date',
|
|
400
399
|
};
|
|
401
400
|
};
|
|
402
401
|
|
|
@@ -470,6 +469,8 @@ const App = () => {
|
|
|
470
469
|
});
|
|
471
470
|
const setStateBatch = (updates) =>
|
|
472
471
|
dispatch({ type: APP_STATE_ACTION, payload: { updates } });
|
|
472
|
+
const resetSchemaDerivedEditState = () =>
|
|
473
|
+
dispatch({ type: REFERENCE_SCHEMA_CHANGED_ACTION });
|
|
473
474
|
|
|
474
475
|
const setSnapshot = (valueOrUpdater) =>
|
|
475
476
|
setAppState('snapshot', valueOrUpdater);
|
|
@@ -517,6 +518,10 @@ const App = () => {
|
|
|
517
518
|
setAppState('codexVersion', valueOrUpdater);
|
|
518
519
|
const setCodexVersionStatus = (valueOrUpdater) =>
|
|
519
520
|
setAppState('codexVersionStatus', valueOrUpdater);
|
|
521
|
+
const setIsSchemaCheckInProgress = (valueOrUpdater) =>
|
|
522
|
+
setAppState('isSchemaCheckInProgress', valueOrUpdater);
|
|
523
|
+
const setSchemaCheckStatusText = (valueOrUpdater) =>
|
|
524
|
+
setAppState('schemaCheckStatusText', valueOrUpdater);
|
|
520
525
|
const {
|
|
521
526
|
snapshot,
|
|
522
527
|
snapshotByFileId,
|
|
@@ -538,6 +543,8 @@ const App = () => {
|
|
|
538
543
|
showHelp,
|
|
539
544
|
codexVersion,
|
|
540
545
|
codexVersionStatus,
|
|
546
|
+
isSchemaCheckInProgress,
|
|
547
|
+
schemaCheckStatusText,
|
|
541
548
|
} = state;
|
|
542
549
|
commandInputRef.current = String(commandInput || '');
|
|
543
550
|
const { exit } = useApp();
|
|
@@ -552,6 +559,10 @@ const App = () => {
|
|
|
552
559
|
: APP_MODES.BROWSE;
|
|
553
560
|
|
|
554
561
|
useEffect(() => {
|
|
562
|
+
if (isSchemaCheckInProgress) {
|
|
563
|
+
return undefined;
|
|
564
|
+
}
|
|
565
|
+
|
|
555
566
|
let isCancelled = false;
|
|
556
567
|
|
|
557
568
|
const loadVersionStatus = async () => {
|
|
@@ -570,9 +581,56 @@ const App = () => {
|
|
|
570
581
|
await ensureLatestConfiguratorVersion(commands.npmCommand);
|
|
571
582
|
};
|
|
572
583
|
|
|
584
|
+
setCodexVersionStatus('Checking Codex version');
|
|
573
585
|
loadVersionStatus();
|
|
574
586
|
ensureLatestConfigurator();
|
|
575
587
|
|
|
588
|
+
return () => {
|
|
589
|
+
isCancelled = true;
|
|
590
|
+
};
|
|
591
|
+
}, [isSchemaCheckInProgress]);
|
|
592
|
+
|
|
593
|
+
useEffect(() => {
|
|
594
|
+
let isCancelled = false;
|
|
595
|
+
|
|
596
|
+
const syncSchemaReference = async () => {
|
|
597
|
+
const checkStartedAt = Date.now();
|
|
598
|
+
const updateSchemaStatus = (status) => {
|
|
599
|
+
if (isCancelled) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
setSchemaCheckStatusText(status);
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
await syncReferenceSchemaAtStartup({
|
|
607
|
+
mainConfigPath: initialMainSnapshot.path,
|
|
608
|
+
onSchemaChange: () => {
|
|
609
|
+
if (isCancelled) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
resetSchemaDerivedEditState();
|
|
614
|
+
},
|
|
615
|
+
onStatus: updateSchemaStatus,
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
const elapsed = Date.now() - checkStartedAt;
|
|
619
|
+
const remainingVisibleMs = SCHEMA_STATUS_MIN_VISIBLE_MS - elapsed;
|
|
620
|
+
if (remainingVisibleMs > 0) {
|
|
621
|
+
await waitForMilliseconds(remainingVisibleMs);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (isCancelled) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
setSchemaCheckStatusText('');
|
|
629
|
+
setIsSchemaCheckInProgress(false);
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
syncSchemaReference();
|
|
633
|
+
|
|
576
634
|
return () => {
|
|
577
635
|
isCancelled = true;
|
|
578
636
|
};
|
|
@@ -822,13 +880,14 @@ const App = () => {
|
|
|
822
880
|
});
|
|
823
881
|
};
|
|
824
882
|
|
|
825
|
-
const
|
|
883
|
+
const beginScalarEditing = (target, targetPath, scalarType) => {
|
|
826
884
|
setEditError('');
|
|
827
885
|
setEditMode({
|
|
828
|
-
mode: '
|
|
886
|
+
mode: 'scalar',
|
|
887
|
+
scalarType,
|
|
829
888
|
path: targetPath,
|
|
830
|
-
draftValue:
|
|
831
|
-
|
|
889
|
+
draftValue:
|
|
890
|
+
typeof target.value === 'undefined' ? '' : String(target.value),
|
|
832
891
|
});
|
|
833
892
|
};
|
|
834
893
|
|
|
@@ -961,15 +1020,24 @@ const App = () => {
|
|
|
961
1020
|
setEditError('');
|
|
962
1021
|
};
|
|
963
1022
|
|
|
964
|
-
const
|
|
965
|
-
if (!editMode || editMode.mode !== '
|
|
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);
|
|
966
1034
|
return;
|
|
967
1035
|
}
|
|
968
1036
|
|
|
969
1037
|
const nextData = setValueAtPath(
|
|
970
1038
|
snapshot.ok ? snapshot.data : {},
|
|
971
1039
|
editMode.path,
|
|
972
|
-
|
|
1040
|
+
parsedValue.value,
|
|
973
1041
|
);
|
|
974
1042
|
if (!ensureAgentConfigFile(nextData, editMode.path)) {
|
|
975
1043
|
return;
|
|
@@ -1062,17 +1130,13 @@ const App = () => {
|
|
|
1062
1130
|
undefined,
|
|
1063
1131
|
'value',
|
|
1064
1132
|
) || [];
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1133
|
+
if (requiredOptions.length > 0) {
|
|
1134
|
+
return requiredOptions[0];
|
|
1135
|
+
}
|
|
1068
1136
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
)
|
|
1073
|
-
) {
|
|
1074
|
-
return '';
|
|
1075
|
-
}
|
|
1137
|
+
if (getReferenceScalarType(requiredPath) === 'string') {
|
|
1138
|
+
return '';
|
|
1139
|
+
}
|
|
1076
1140
|
|
|
1077
1141
|
return {};
|
|
1078
1142
|
},
|
|
@@ -1273,28 +1337,28 @@ const App = () => {
|
|
|
1273
1337
|
setCommandInput,
|
|
1274
1338
|
getCommandInput: () => commandInputRef.current,
|
|
1275
1339
|
setCommandMessage,
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1340
|
+
setEditError,
|
|
1341
|
+
beginAddIdEditing,
|
|
1342
|
+
beginScalarEditing,
|
|
1343
|
+
beginVariantEditing,
|
|
1344
|
+
beginEditing,
|
|
1345
|
+
beginFileSwitchMode,
|
|
1346
|
+
applyFileSwitch,
|
|
1347
|
+
applyScalarEdit,
|
|
1348
|
+
applyAddId,
|
|
1349
|
+
applyVariantEdit,
|
|
1350
|
+
applyEdit,
|
|
1287
1351
|
applyBooleanToggle,
|
|
1288
1352
|
unsetValueAtPath,
|
|
1289
1353
|
openPathView,
|
|
1290
1354
|
reloadActiveConfig,
|
|
1291
1355
|
getConfigOptions: getConfigOptions,
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1356
|
+
getConfigVariantMeta,
|
|
1357
|
+
getNodeAtPath,
|
|
1358
|
+
buildRows,
|
|
1359
|
+
getScalarEditType,
|
|
1360
|
+
isCustomIdTableRow,
|
|
1361
|
+
resolveMixedVariantBackNavigationPath,
|
|
1298
1362
|
adjustScrollForSelection,
|
|
1299
1363
|
getSavedIndex,
|
|
1300
1364
|
readActiveConfigSnapshot,
|
|
@@ -1447,6 +1511,8 @@ const App = () => {
|
|
|
1447
1511
|
React.createElement(StatusLine, {
|
|
1448
1512
|
codexVersion,
|
|
1449
1513
|
codexVersionStatus,
|
|
1514
|
+
isSchemaCheckInProgress,
|
|
1515
|
+
schemaCheckStatusText,
|
|
1450
1516
|
activeConfigFile,
|
|
1451
1517
|
appMode,
|
|
1452
1518
|
}),
|
package/package.json
CHANGED
package/src/appState.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const APP_STATE_ACTION = 'APP_STATE_ACTION';
|
|
2
|
+
export const REFERENCE_SCHEMA_CHANGED_ACTION = 'REFERENCE_SCHEMA_CHANGED_ACTION';
|
|
2
3
|
|
|
3
4
|
export const APP_MODES = {
|
|
4
5
|
BROWSE: 'browse',
|
|
@@ -9,6 +10,14 @@ export const APP_MODES = {
|
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
export const appStateReducer = (state, action) => {
|
|
13
|
+
if (action.type === REFERENCE_SCHEMA_CHANGED_ACTION) {
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
editMode: null,
|
|
17
|
+
editError: '',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
if (action.type !== APP_STATE_ACTION) {
|
|
13
22
|
return state;
|
|
14
23
|
}
|
|
@@ -66,4 +75,6 @@ export const buildInitialAppState = (initialMainSnapshot, initialCatalog, initia
|
|
|
66
75
|
showHelp: false,
|
|
67
76
|
codexVersion: 'version loading...',
|
|
68
77
|
codexVersionStatus: '',
|
|
78
|
+
isSchemaCheckInProgress: true,
|
|
79
|
+
schemaCheckStatusText: 'Checking schema',
|
|
69
80
|
});
|
|
@@ -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
|
|
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
|
-
|
|
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 === '
|
|
475
|
-
?
|
|
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/components/Header.js
CHANGED
package/src/configFeatures.js
CHANGED
|
@@ -35,25 +35,24 @@ const buildFeatureDefinitionFromReference = (key) => {
|
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return acc;
|
|
48
|
-
}, {});
|
|
38
|
+
const buildFeatureDefinitionMap = () =>
|
|
39
|
+
getReferenceFeatureKeys()
|
|
40
|
+
.map((key) => buildFeatureDefinitionFromReference(key))
|
|
41
|
+
.filter(Boolean)
|
|
42
|
+
.reduce((acc, definition) => {
|
|
43
|
+
acc[definition.key] = definition;
|
|
44
|
+
return acc;
|
|
45
|
+
}, {});
|
|
49
46
|
|
|
50
47
|
export const getConfigFeatureKeys = () => {
|
|
51
|
-
return
|
|
48
|
+
return getReferenceFeatureKeys();
|
|
52
49
|
};
|
|
53
50
|
|
|
54
|
-
export const getConfigFeatureDefinition = (key) =>
|
|
51
|
+
export const getConfigFeatureDefinition = (key) =>
|
|
52
|
+
buildFeatureDefinitionMap()[String(key || '').trim()];
|
|
55
53
|
|
|
56
54
|
export const getConfigFeatureDefinitionOrFallback = (key) => {
|
|
55
|
+
const normalizedKey = String(key || '').trim();
|
|
57
56
|
if (!key) {
|
|
58
57
|
return {
|
|
59
58
|
short: `${prettifyFeatureName(String(key))}`,
|
|
@@ -64,9 +63,9 @@ export const getConfigFeatureDefinitionOrFallback = (key) => {
|
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
return (
|
|
67
|
-
|
|
68
|
-
key,
|
|
69
|
-
short: prettifyFeatureName(
|
|
66
|
+
getConfigFeatureDefinition(normalizedKey) || {
|
|
67
|
+
key: normalizedKey,
|
|
68
|
+
short: prettifyFeatureName(normalizedKey),
|
|
70
69
|
usage: 'This configured key is not in the official feature list.',
|
|
71
70
|
defaultValue: false,
|
|
72
71
|
isDocumented: false,
|
package/src/configReference.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
|
-
const
|
|
4
|
+
const BUNDLED_CONFIG_SCHEMA_DATA = require('./reference/config-schema.json');
|
|
5
5
|
|
|
6
6
|
const ROOT_PLACEHOLDER = '<name>';
|
|
7
7
|
const PLACEHOLDER_SEGMENT = /^<[^>]+>$/;
|
|
@@ -38,6 +38,8 @@ const normalizeType = (type) => String(type || '').replace(/\s+/g, ' ').trim();
|
|
|
38
38
|
const isObject = (value) =>
|
|
39
39
|
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
40
40
|
|
|
41
|
+
const buildSchemaRevision = (schema) => JSON.stringify(schema);
|
|
42
|
+
|
|
41
43
|
const isPrimitiveValue = (type) =>
|
|
42
44
|
type === 'string' || type === 'number' || type === 'integer' || type === 'boolean';
|
|
43
45
|
|
|
@@ -966,9 +968,62 @@ const fullPathMatches = (referencePath, actualPath) =>
|
|
|
966
968
|
const countConcreteSegments = (segments) =>
|
|
967
969
|
segments.reduce((count, segment) => count + (isPlaceholderSegment(segment) ? 0 : 1), 0);
|
|
968
970
|
|
|
969
|
-
const
|
|
971
|
+
const buildFeatureKeys = (referenceOptions) => [
|
|
972
|
+
...new Set(
|
|
973
|
+
referenceOptions
|
|
974
|
+
.filter(
|
|
975
|
+
(option) =>
|
|
976
|
+
option.keyPath.length === 2 &&
|
|
977
|
+
option.keyPath[0] === 'features' &&
|
|
978
|
+
!isPlaceholderSegment(option.keyPath[1])
|
|
979
|
+
)
|
|
980
|
+
.map((option) => option.keyPath[1])
|
|
981
|
+
),
|
|
982
|
+
].sort((left, right) => left.localeCompare(right));
|
|
983
|
+
|
|
984
|
+
const prepareReferenceState = (schema) => {
|
|
985
|
+
if (!isObject(schema)) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const referenceOptions = buildReferenceOptions(schema);
|
|
990
|
+
if (!Array.isArray(referenceOptions) || referenceOptions.length === 0) {
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
return {
|
|
995
|
+
schema,
|
|
996
|
+
revision: buildSchemaRevision(schema),
|
|
997
|
+
referenceOptions,
|
|
998
|
+
optionsByKey: new Map(referenceOptions.map((option) => [option.key, option])),
|
|
999
|
+
featureKeys: buildFeatureKeys(referenceOptions),
|
|
1000
|
+
};
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
let activeReferenceState = prepareReferenceState(BUNDLED_CONFIG_SCHEMA_DATA);
|
|
1004
|
+
if (!activeReferenceState) {
|
|
1005
|
+
throw new Error('Bundled config schema is invalid.');
|
|
1006
|
+
}
|
|
970
1007
|
|
|
971
|
-
const
|
|
1008
|
+
const getReferenceState = () => activeReferenceState;
|
|
1009
|
+
|
|
1010
|
+
export const isReferenceSchemaValid = (schema) => Boolean(prepareReferenceState(schema));
|
|
1011
|
+
|
|
1012
|
+
export const getReferenceSchemaRevision = () => getReferenceState().revision;
|
|
1013
|
+
|
|
1014
|
+
export const setReferenceSchema = (schema) => {
|
|
1015
|
+
const nextState = prepareReferenceState(schema);
|
|
1016
|
+
if (!nextState) {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
if (activeReferenceState?.revision === nextState.revision) {
|
|
1021
|
+
return true;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
activeReferenceState = nextState;
|
|
1025
|
+
return true;
|
|
1026
|
+
};
|
|
972
1027
|
|
|
973
1028
|
const mergeDefinition = (map, definition) => {
|
|
974
1029
|
const existing = map.get(definition.key);
|
|
@@ -988,6 +1043,7 @@ const mergeDefinition = (map, definition) => {
|
|
|
988
1043
|
};
|
|
989
1044
|
|
|
990
1045
|
export const getReferenceOptionForPath = (pathSegments) => {
|
|
1046
|
+
const { optionsByKey, referenceOptions } = getReferenceState();
|
|
991
1047
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
992
1048
|
if (normalizedPath.length === 0) {
|
|
993
1049
|
return null;
|
|
@@ -1016,7 +1072,7 @@ export const getReferenceOptionForPath = (pathSegments) => {
|
|
|
1016
1072
|
return candidates[0];
|
|
1017
1073
|
};
|
|
1018
1074
|
|
|
1019
|
-
const getVariantObjectPaths = (normalizedPath) => {
|
|
1075
|
+
const getVariantObjectPaths = (normalizedPath, referenceOptions) => {
|
|
1020
1076
|
const depth = normalizedPath.length;
|
|
1021
1077
|
const paths = referenceOptions
|
|
1022
1078
|
.filter((option) => pathPrefixMatches(option.keyPath, normalizedPath))
|
|
@@ -1028,6 +1084,7 @@ const getVariantObjectPaths = (normalizedPath) => {
|
|
|
1028
1084
|
};
|
|
1029
1085
|
|
|
1030
1086
|
export const getReferenceVariantForPath = (pathSegments = []) => {
|
|
1087
|
+
const { referenceOptions } = getReferenceState();
|
|
1031
1088
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
1032
1089
|
if (normalizedPath.length === 0) {
|
|
1033
1090
|
return null;
|
|
@@ -1061,11 +1118,12 @@ export const getReferenceVariantForPath = (pathSegments = []) => {
|
|
|
1061
1118
|
: {},
|
|
1062
1119
|
}))
|
|
1063
1120
|
: [],
|
|
1064
|
-
objectSchemaPaths: getVariantObjectPaths(normalizedPath),
|
|
1121
|
+
objectSchemaPaths: getVariantObjectPaths(normalizedPath, referenceOptions),
|
|
1065
1122
|
};
|
|
1066
1123
|
};
|
|
1067
1124
|
|
|
1068
1125
|
export const getReferenceTableDefinitions = (pathSegments = []) => {
|
|
1126
|
+
const { referenceOptions } = getReferenceState();
|
|
1069
1127
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
1070
1128
|
const childDefinitions = new Map();
|
|
1071
1129
|
const depth = normalizedPath.length;
|
|
@@ -1099,22 +1157,10 @@ export const getReferenceTableDefinitions = (pathSegments = []) => {
|
|
|
1099
1157
|
|
|
1100
1158
|
export const getReferenceRootDefinitions = () => getReferenceTableDefinitions([]);
|
|
1101
1159
|
|
|
1102
|
-
const
|
|
1103
|
-
...new Set(
|
|
1104
|
-
referenceOptions
|
|
1105
|
-
.filter(
|
|
1106
|
-
(option) =>
|
|
1107
|
-
option.keyPath.length === 2 &&
|
|
1108
|
-
option.keyPath[0] === 'features' &&
|
|
1109
|
-
!isPlaceholderSegment(option.keyPath[1])
|
|
1110
|
-
)
|
|
1111
|
-
.map((option) => option.keyPath[1])
|
|
1112
|
-
),
|
|
1113
|
-
].sort((left, right) => left.localeCompare(right));
|
|
1114
|
-
|
|
1115
|
-
export const getReferenceFeatureKeys = () => featureKeys;
|
|
1160
|
+
export const getReferenceFeatureKeys = () => [...getReferenceState().featureKeys];
|
|
1116
1161
|
|
|
1117
1162
|
export const getReferenceCustomIdPlaceholder = (pathSegments = []) => {
|
|
1163
|
+
const { referenceOptions } = getReferenceState();
|
|
1118
1164
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
1119
1165
|
const depth = normalizedPath.length;
|
|
1120
1166
|
const placeholders = new Set();
|
|
@@ -1139,6 +1185,7 @@ export const getReferenceCustomIdPlaceholder = (pathSegments = []) => {
|
|
|
1139
1185
|
};
|
|
1140
1186
|
|
|
1141
1187
|
export const getReferenceDescendantOptions = (pathSegments = []) => {
|
|
1188
|
+
const { referenceOptions } = getReferenceState();
|
|
1142
1189
|
const normalizedPath = normalizeSegments(pathSegments);
|
|
1143
1190
|
|
|
1144
1191
|
return referenceOptions
|