@ham2k/extension-sdk 0.12.12 → 0.12.13
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/dist/activityExports.js +4 -2
- package/dist/index.d.ts +23 -1
- package/dist/settingsPanels.js +6 -3
- package/docs/distribution.md +29 -6
- package/docs/forms.md +11 -6
- package/docs/hooks.md +48 -12
- package/docs/settings.md +21 -7
- package/package.json +1 -1
package/dist/activityExports.js
CHANGED
|
@@ -53,8 +53,10 @@ function activityExportHook(rules) {
|
|
|
53
53
|
filename: filenameFor(operation, qsos ?? [], ref.ref, compactFilenames),
|
|
54
54
|
icon: rules.icon,
|
|
55
55
|
selectedByDefault: true,
|
|
56
|
-
// Segment slicing: only the contacts made while
|
|
57
|
-
|
|
56
|
+
// Segment slicing: only the contacts made while THIS reference was
|
|
57
|
+
// being activated.
|
|
58
|
+
refType: rules.activationType,
|
|
59
|
+
ref: ref.ref
|
|
58
60
|
}));
|
|
59
61
|
},
|
|
60
62
|
async generateExport(args, ctx) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ham2k/extension-sdk 0.12.
|
|
1
|
+
// @ham2k/extension-sdk 0.12.13
|
|
2
2
|
/** Experimental v1 panel scene contract. All coordinates are in the scene's viewBox. */
|
|
3
3
|
export interface PanelScene {
|
|
4
4
|
version: 1;
|
|
@@ -597,6 +597,7 @@ export interface HookContext {
|
|
|
597
597
|
distanceUnits?: string;
|
|
598
598
|
decimalSeparator?: string;
|
|
599
599
|
groupingSeparator?: string;
|
|
600
|
+
dateOrder?: "mdy" | "dmy" | "ymd";
|
|
600
601
|
gridPrecision?: number;
|
|
601
602
|
edition?: string;
|
|
602
603
|
enabledExperiments?: string[];
|
|
@@ -703,6 +704,7 @@ export interface PostResult {
|
|
|
703
704
|
ok: boolean;
|
|
704
705
|
message?: string;
|
|
705
706
|
userMessage?: ResultMessage;
|
|
707
|
+
skipped?: boolean;
|
|
706
708
|
}
|
|
707
709
|
export interface ResultMessage {
|
|
708
710
|
presentation: "status" | "dialog";
|
|
@@ -869,6 +871,7 @@ export interface TextInputDescriptor {
|
|
|
869
871
|
}[];
|
|
870
872
|
numeric?: boolean;
|
|
871
873
|
uppercase?: boolean;
|
|
874
|
+
numbersRowKey?: string;
|
|
872
875
|
maxLength?: number;
|
|
873
876
|
suggestedValue?: string;
|
|
874
877
|
}
|
|
@@ -890,6 +893,7 @@ export interface OptionsInputDescriptor {
|
|
|
890
893
|
minCharsForSuggestions?: number;
|
|
891
894
|
placeholder?: string;
|
|
892
895
|
uppercase?: boolean;
|
|
896
|
+
numbersRowKey?: string;
|
|
893
897
|
maxLength?: number;
|
|
894
898
|
suggestedValue?: string;
|
|
895
899
|
pattern?: string;
|
|
@@ -910,6 +914,7 @@ export interface RefListInputDescriptor {
|
|
|
910
914
|
replacement: string;
|
|
911
915
|
flags?: string;
|
|
912
916
|
}[];
|
|
917
|
+
numbersRowKey?: string;
|
|
913
918
|
}
|
|
914
919
|
export interface ActivitySuggestion extends Ref {
|
|
915
920
|
distance?: number;
|
|
@@ -1004,6 +1009,7 @@ export interface ExportRequest {
|
|
|
1004
1009
|
operation: Record<string, JSONValue>;
|
|
1005
1010
|
qsos: Record<string, JSONValue>[];
|
|
1006
1011
|
segments?: OperationSegmentPayload[];
|
|
1012
|
+
events?: Record<string, JSONValue>[];
|
|
1007
1013
|
exportType?: string;
|
|
1008
1014
|
exportKey?: string;
|
|
1009
1015
|
compactFilenames?: boolean;
|
|
@@ -1037,6 +1043,7 @@ export interface ExportOption {
|
|
|
1037
1043
|
priority?: number;
|
|
1038
1044
|
selectedByDefault?: boolean;
|
|
1039
1045
|
refType?: string;
|
|
1046
|
+
ref?: string;
|
|
1040
1047
|
}
|
|
1041
1048
|
export interface ExportHook {
|
|
1042
1049
|
getExportTypes?(args: Record<string, never>, ctx: HookContext): Promise<ExportTypeDefinition[]>;
|
|
@@ -1278,9 +1285,21 @@ export interface CommandAction {
|
|
|
1278
1285
|
kind: "start" | "break" | "stop";
|
|
1279
1286
|
setLocation?: boolean;
|
|
1280
1287
|
};
|
|
1288
|
+
findInLog?: {
|
|
1289
|
+
query?: string;
|
|
1290
|
+
};
|
|
1281
1291
|
setCallField?: {
|
|
1282
1292
|
text: string;
|
|
1283
1293
|
};
|
|
1294
|
+
setTime?: {
|
|
1295
|
+
now?: boolean;
|
|
1296
|
+
deltaMillis?: number;
|
|
1297
|
+
hour?: number;
|
|
1298
|
+
minute?: number;
|
|
1299
|
+
second?: number;
|
|
1300
|
+
month?: number;
|
|
1301
|
+
day?: number;
|
|
1302
|
+
};
|
|
1284
1303
|
reloadDataFiles?: {
|
|
1285
1304
|
target: string;
|
|
1286
1305
|
};
|
|
@@ -1685,6 +1704,7 @@ export interface FormField {
|
|
|
1685
1704
|
itemFields?: FormField[];
|
|
1686
1705
|
itemTitle?: string;
|
|
1687
1706
|
orderable?: boolean;
|
|
1707
|
+
itemSwitch?: string;
|
|
1688
1708
|
inline?: boolean;
|
|
1689
1709
|
itemLabel?: string;
|
|
1690
1710
|
defaultValue?: any[];
|
|
@@ -1786,6 +1806,7 @@ export interface SettingsPanelDescriptor {
|
|
|
1786
1806
|
order?: number;
|
|
1787
1807
|
sidebar?: boolean;
|
|
1788
1808
|
dataFilesSection?: boolean;
|
|
1809
|
+
section?: string;
|
|
1789
1810
|
}
|
|
1790
1811
|
export interface SettingsField extends Omit<FormField, "validate" | "transform" | "itemFields"> {
|
|
1791
1812
|
pattern?: string;
|
|
@@ -1801,6 +1822,7 @@ export interface SimpleSettingsPanel {
|
|
|
1801
1822
|
order?: number;
|
|
1802
1823
|
sidebar?: boolean;
|
|
1803
1824
|
dataFilesSection?: boolean;
|
|
1825
|
+
section?: string;
|
|
1804
1826
|
fields: (SettingsField | FormActionElement)[];
|
|
1805
1827
|
}
|
|
1806
1828
|
export interface DynamicSettingsPanel {
|
package/dist/settingsPanels.js
CHANGED
|
@@ -18,7 +18,8 @@ function settingsPanelEntriesFor(record, descriptors) {
|
|
|
18
18
|
order: hook.order ?? null,
|
|
19
19
|
kind: "simple",
|
|
20
20
|
sidebar: hook.sidebar ?? true,
|
|
21
|
-
dataFilesSection: hook.dataFilesSection ?? false
|
|
21
|
+
dataFilesSection: hook.dataFilesSection ?? false,
|
|
22
|
+
section: hook.section ?? null
|
|
22
23
|
}
|
|
23
24
|
];
|
|
24
25
|
}
|
|
@@ -33,7 +34,8 @@ function settingsPanelEntriesFor(record, descriptors) {
|
|
|
33
34
|
order: d.order ?? null,
|
|
34
35
|
kind: "dynamic",
|
|
35
36
|
sidebar: d.sidebar ?? true,
|
|
36
|
-
dataFilesSection: d.dataFilesSection ?? false
|
|
37
|
+
dataFilesSection: d.dataFilesSection ?? false,
|
|
38
|
+
section: d.section ?? null
|
|
37
39
|
}));
|
|
38
40
|
}
|
|
39
41
|
return [
|
|
@@ -46,7 +48,8 @@ function settingsPanelEntriesFor(record, descriptors) {
|
|
|
46
48
|
order: null,
|
|
47
49
|
kind: "dynamic",
|
|
48
50
|
sidebar: true,
|
|
49
|
-
dataFilesSection: false
|
|
51
|
+
dataFilesSection: false,
|
|
52
|
+
section: null
|
|
50
53
|
}
|
|
51
54
|
];
|
|
52
55
|
}
|
package/docs/distribution.md
CHANGED
|
@@ -388,7 +388,7 @@ The app builds in only its **core** extensions — the ones that are the app
|
|
|
388
388
|
itself: `adif`, `scoring`, `lookups`, `templates`, the command sets and
|
|
389
389
|
`ham2k-lofi` sync. Every other extension is a catalog bundle, and the app ships
|
|
390
390
|
some of them already packaged under `app/assets/preloaded-extensions/`,
|
|
391
|
-
installing
|
|
391
|
+
installing the ones the operator has switched on into the ordinary store. Afterwards they are
|
|
392
392
|
ordinary installed extensions — the same rows, the same update check, the same
|
|
393
393
|
Update and uninstall — so an operator starts with a working app and the catalog
|
|
394
394
|
can move it forward from there.
|
|
@@ -416,7 +416,30 @@ ship pre-loads that fail the check ([releases.md §6.4](https://github.com/ham2k
|
|
|
416
416
|
They are installed once, not every launch. A record of the highest version
|
|
417
417
|
ever pre-loaded per key is what makes that true: an app update carrying a
|
|
418
418
|
newer bundle installs it, a catalog release already ahead of it is left
|
|
419
|
-
alone, and a key the operator uninstalled stays gone
|
|
419
|
+
alone, and a key the operator uninstalled stays gone — its uninstall leaves
|
|
420
|
+
its switch off, which is what the pre-load asks before installing a newer copy.
|
|
421
|
+
|
|
422
|
+
**Only what is switched on is installed** — by the operator's choice for the
|
|
423
|
+
key, else its built-in's, else on. One switched off would be a dead row in the
|
|
424
|
+
Extensions panel, and for a new install that is most of them. Skipped, it is
|
|
425
|
+
not recorded as offered, so switching on one never offered brings it in; one
|
|
426
|
+
already offered comes back from the catalog. A shipped key's switch is kept
|
|
427
|
+
after it leaves the store, and an uninstall or a Forget leaves it off, so an
|
|
428
|
+
app update carrying a newer copy does not bring back one the operator
|
|
429
|
+
removed. Nothing is
|
|
430
|
+
installed until onboarding has been answered, since the activities it asks
|
|
431
|
+
about are pre-loads: installed on their defaults first, whichever the operator
|
|
432
|
+
then turns down would stay installed and switched off. Onboarding settles them
|
|
433
|
+
as it finishes (`settleOnboardingExtensions`), as `ONBOARD_AS` does.
|
|
434
|
+
|
|
435
|
+
An install can hold every bundle the app ships, whatever its switch. It takes
|
|
436
|
+
the pre-loaded bundles and built-ins' twins it holds switched off out of the
|
|
437
|
+
store once (`extensionsSwitchedOffRemoved`), keeping their settings,
|
|
438
|
+
credentials and switch for a reinstall, and records that done only once it has
|
|
439
|
+
read the whole store. Other catalog extensions are the operator's own and stay,
|
|
440
|
+
as do file installs. A replayed onboarding (DEVONBOARD) removes nothing. A
|
|
441
|
+
catalog install the operator makes is always switched on, whatever switch it
|
|
442
|
+
inherits.
|
|
420
443
|
|
|
421
444
|
An installed bundle may not claim a key the app builds in, and a pre-load is
|
|
422
445
|
no exception: it goes through the same door as any other install.
|
|
@@ -445,8 +468,8 @@ a callsign before its extension state was first recorded
|
|
|
445
468
|
a build without them, unasked: there is nothing left to run instead. The boot's
|
|
446
469
|
catch-up (`catchUpOnCatalogExtensions`) carries each built-in's on/off choice
|
|
447
470
|
onto its twin, over whatever an earlier attempt left there; installs the
|
|
448
|
-
pre-loads, which carry the built-in's settings, panel values and
|
|
449
|
-
a twin's first install
|
|
471
|
+
pre-loads switched on, which carry the built-in's settings, panel values and
|
|
472
|
+
credentials on a twin's first install; then
|
|
450
473
|
fetches the twin of any other built-in the operator had switched on. It records
|
|
451
474
|
the move done once the pre-load has run, so a boot killed half way carries the
|
|
452
475
|
choices again next time. It needs no network while every twin is pre-loaded.
|
|
@@ -469,8 +492,8 @@ refuses the bundle itself: a keychain that would not open or a full disk is
|
|
|
469
492
|
tried again next launch, and meanwhile the adoption fetches the catalog's copy
|
|
470
493
|
if the operator had it on. A twin the pre-load did install is never fetched
|
|
471
494
|
back after the operator uninstalls it. A choice stored under the pre-load's own
|
|
472
|
-
name — `ham2k-lookup`'s, which kept its name —
|
|
473
|
-
stale.
|
|
495
|
+
name — `ham2k-lookup`'s, which kept its name — decides whether it is installed,
|
|
496
|
+
and is kept rather than dropped as stale.
|
|
474
497
|
|
|
475
498
|
**What does not come along.** A few built-ins have no twin on purpose — `qp`,
|
|
476
499
|
which the catalog carries as one extension per party instead, and `stateparks`,
|
package/docs/forms.md
CHANGED
|
@@ -66,15 +66,20 @@ export interface FormField {
|
|
|
66
66
|
// Record<string, any>[]; each item is shaped like itemFields. By default
|
|
67
67
|
// the field renders as one summary row (from itemTitle, templated
|
|
68
68
|
// against the item's values, e.g. "${name}" — NOT "{{name}}") that opens
|
|
69
|
-
// an editor dialog with per-item rows and
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
69
|
+
// an editor dialog with per-item rows and Add; `inline: true` instead
|
|
70
|
+
// renders those rows (with a reorder handle per row, and an Add row)
|
|
71
|
+
// directly in the form, skipping that outer dialog — for a list that's the
|
|
72
|
+
// main content of its own section. Either way, tapping a row (or Add)
|
|
73
|
+
// opens the same itemFields editor dialog (the form renderer recursively,
|
|
74
|
+
// scoped to itemFields), whose footer holds Delete for an existing item,
|
|
75
|
+
// which asks the operator to confirm.
|
|
75
76
|
itemFields?: FormField[];
|
|
76
77
|
itemTitle?: string;
|
|
77
78
|
orderable?: boolean;
|
|
79
|
+
// The key of an on/off value in each item, set from a switch at the end of
|
|
80
|
+
// the item's row; leave it out of itemFields. An item without the key reads
|
|
81
|
+
// as on. An app that predates this shows no switch.
|
|
82
|
+
itemSwitch?: string;
|
|
78
83
|
inline?: boolean;
|
|
79
84
|
// Singular noun for one item (e.g. "custom note file"), used in the
|
|
80
85
|
// editor dialog's Add/Edit titles and the Add button instead of `label`
|
package/docs/hooks.md
CHANGED
|
@@ -309,7 +309,7 @@ interface SpotsHook {
|
|
|
309
309
|
// Spot: {their: {call}, freq?, band?, mode?, refs?: Ref[],
|
|
310
310
|
// spot: {timeInMillis, source, label?, sourceInfo?}}
|
|
311
311
|
// SpotEligibility: {enabled, icon?}
|
|
312
|
-
// PostResult: {ok, message?}
|
|
312
|
+
// PostResult: {ok, message?, userMessage?, skipped?}
|
|
313
313
|
// PostSelfSpotRequest: {operation, freq, mode?, comment?}
|
|
314
314
|
// PostOtherSpotRequest: {qso, comment?, spotterCall?}
|
|
315
315
|
```
|
|
@@ -330,6 +330,15 @@ directly to the POTA API, no auth required). `sota`'s spot API requires an
|
|
|
330
330
|
authenticated account app-halo doesn't have yet, so `sota` stays
|
|
331
331
|
fetch-only for now.
|
|
332
332
|
|
|
333
|
+
A poster whose own rules decline a particular spot — a Discord channel whose
|
|
334
|
+
message template renders blank for it — answers `{ok: true, skipped: true}`.
|
|
335
|
+
Its icon stays neutral gray instead of lighting as sent, and no failure is
|
|
336
|
+
reported. The Spotting chip's color counts only the sources that did not skip.
|
|
337
|
+
When every one did, the chip stays neutral and the operator is told so, in
|
|
338
|
+
each source's `message` when it gives one; that spot is never what
|
|
339
|
+
auto-respotting repeats, and a round of it every source skips ends it.
|
|
340
|
+
`skipped` without `ok` is still a failure.
|
|
341
|
+
|
|
333
342
|
## ✅ `cluster` — the DX cluster connection
|
|
334
343
|
|
|
335
344
|
```ts
|
|
@@ -381,11 +390,17 @@ interface ExportHook {
|
|
|
381
390
|
generateExport(args: ExportRequest, ctx): Promise<ExportResult>
|
|
382
391
|
}
|
|
383
392
|
// ExportOptionsRequest: {operation, qsos, compactFilenames?}
|
|
384
|
-
// ExportOption: {exportType, exportKey?, format, label, filename?, icon?, color?, refType?, priority?, selectedByDefault?}
|
|
385
|
-
// ExportRequest: {operation, qsos, exportType?, exportKey?, compactFilenames?} — full QSON, one coarse call
|
|
393
|
+
// ExportOption: {exportType, exportKey?, format, label, filename?, icon?, color?, refType?, ref?, priority?, selectedByDefault?}
|
|
394
|
+
// ExportRequest: {operation, qsos, segments?, events?, exportType?, exportKey?, compactFilenames?} — full QSON, one coarse call
|
|
386
395
|
// ExportResult: {filename, mimeType, content}
|
|
387
396
|
```
|
|
388
397
|
|
|
398
|
+
`qsos` holds contacts alone. The log's event rows — `break`, `start`, notes —
|
|
399
|
+
come separately in `events`, for a file that writes the timeline (the full
|
|
400
|
+
ADIF does, with private data); `segments` is what they resolve to.
|
|
401
|
+
`operation` carries `startAtMillisMin`/`startAtMillisMax`: the span of the
|
|
402
|
+
contacts in `qsos`, not of the whole operation.
|
|
403
|
+
|
|
389
404
|
Two-step flow: the Exports Panel calls `suggestExportOptions` on every
|
|
390
405
|
`export` hook to build its selectable list (a hook that omits it never
|
|
391
406
|
appears in the panel), then calls `generateExport` — passing back the
|
|
@@ -471,6 +486,12 @@ whole-log ADIF — asks for a glyph of its own instead of borrowing one. An
|
|
|
471
486
|
`icon` name that doesn't resolve falls back to the activity's, same as an
|
|
472
487
|
unreadable `color` does.
|
|
473
488
|
|
|
489
|
+
**One reference's file.** An export claiming one reference of its `refType` —
|
|
490
|
+
one park's log, when the operation activated several — names it in `ref`, and
|
|
491
|
+
the core narrows the slice to the segments carrying that reference. Without
|
|
492
|
+
it, a park activated after a break gets every contact made at the park before
|
|
493
|
+
it. `activityExportHook` sets it for you.
|
|
494
|
+
|
|
474
495
|
**Filenames.** Build them with the SDK's `exportFilename` rather than
|
|
475
496
|
spelling one out, passing the request's `compactFilenames` straight through —
|
|
476
497
|
that is what makes the user's "Use compact file names" setting apply to your
|
|
@@ -491,7 +512,7 @@ filename: exportFilename({
|
|
|
491
512
|
Set `filename` on the OPTION, not just on the result: the panel shows the
|
|
492
513
|
option's name, and the core writes the file under it. A name computed again
|
|
493
514
|
in `generateExport` sees a different input — that call is handed only the
|
|
494
|
-
QSOs the option's `refType` covers — so the two would otherwise disagree in
|
|
515
|
+
QSOs the option's `refType` (and `ref`) covers — so the two would otherwise disagree in
|
|
495
516
|
front of the user.
|
|
496
517
|
|
|
497
518
|
## ✅ `ref:<type>` — reference handlers
|
|
@@ -681,14 +702,15 @@ interface ActivityHook {
|
|
|
681
702
|
// The input catalog. A new kind is earned by a different WIDGET, never by
|
|
682
703
|
// different validation — so a pattern-constrained field is `text` with a
|
|
683
704
|
// `pattern`, not a kind of its own (docs/design/contests.md §5.3).
|
|
684
|
-
// {kind: 'refList', refType, placeholder?, pattern?, transforms
|
|
705
|
+
// {kind: 'refList', refType, placeholder?, pattern?, transforms?,
|
|
706
|
+
// numbersRowKey?}
|
|
685
707
|
// {kind: 'text', refType, field, placeholder?, pattern?, transforms?,
|
|
686
|
-
// numeric?, uppercase? /* default true */,
|
|
687
|
-
// suggestedValue?}
|
|
708
|
+
// numeric?, uppercase? /* default true */, numbersRowKey?,
|
|
709
|
+
// maxLength?, suggestedValue?}
|
|
688
710
|
// {kind: 'options', refType, field, options: [{code, name?}], matchOn?,
|
|
689
711
|
// allowFreeform?, preferredCodes?, minCharsForSuggestions?,
|
|
690
712
|
// placeholder?, pattern?, transforms?, numeric?, uppercase?,
|
|
691
|
-
// maxLength?, suggestedValue?}
|
|
713
|
+
// numbersRowKey?, maxLength?, suggestedValue?}
|
|
692
714
|
// {kind: 'serial', refType, field, sequence: {key, scope?, start?, padTo?},
|
|
693
715
|
// editable?}
|
|
694
716
|
// {kind: 'form', refType, form} // operationControls only
|
|
@@ -700,6 +722,12 @@ interface ActivityHook {
|
|
|
700
722
|
// exports as typed. The pattern is HaLo's idea of the program rather than the
|
|
701
723
|
// program's own, so it may never cost the operator a reference.
|
|
702
724
|
//
|
|
725
|
+
// `numbersRowKey` is the one character the mobile numbers row offers after
|
|
726
|
+
// its digits while the field has focus, where the operator would otherwise
|
|
727
|
+
// switch keyboard layers for it. Without it a `refList` field offers `'-'`,
|
|
728
|
+
// which joins most programs' prefixes to their digits, and the other kinds
|
|
729
|
+
// the operator's decimal separator.
|
|
730
|
+
//
|
|
703
731
|
// `text`, `options` and `serial` values round-trip onto a ref of `refType` under
|
|
704
732
|
// `field`, through the same core-owned path refList writes refs — several may
|
|
705
733
|
// share one refType and are merged into a single ref. `suggestedValue` fills the
|
|
@@ -919,6 +947,14 @@ returning several is describing individual records. Declaring both methods is
|
|
|
919
947
|
fine and expected — a hook that does is asked only for its combinations, so
|
|
920
948
|
its plain fields aren't written twice. Implemented by: `pota`.
|
|
921
949
|
|
|
950
|
+
Only a program's **own** export asks for combinations. The full ADIF export is
|
|
951
|
+
one record per contact — the copy the operator keeps, which a reimport or a
|
|
952
|
+
log processor must not read as several contacts — so it asks every hook for
|
|
953
|
+
`fieldsForOneQSO` alone, as app-polo's does. A splitting program answers that
|
|
954
|
+
method too, for the one record — POTA names its first hunted park in `SIG_INFO`
|
|
955
|
+
and every one in `POTA_REF`. A hook answering only combinations is left out of
|
|
956
|
+
the full export, and the kernel logs an error for it as it activates.
|
|
957
|
+
|
|
922
958
|
**Only the export's OWN hook is asked.** A file submitted to one program is
|
|
923
959
|
that program's log, so an export that delegates the ADIF build names itself as
|
|
924
960
|
`mainHandler` in the `ExportRequest` — app-polo's "main handler"
|
|
@@ -992,9 +1028,9 @@ writes the contact's own operator on every record, falling back to the station
|
|
|
992
1028
|
call's base call or the station call itself. This is a
|
|
993
1029
|
deliberate divergence from app-polo, whose contact fields come first and whose
|
|
994
1030
|
main handler's are appended unchecked, so a name both answer is written twice. The **full export**, which asks every
|
|
995
|
-
program on the contact, has no
|
|
996
|
-
|
|
997
|
-
|
|
1031
|
+
program on the contact, has no main handler to order by: hooks are asked in priority order,
|
|
1032
|
+
so which of two programs keeps a shared `MY_SIG` there is not something either
|
|
1033
|
+
of them chose.
|
|
998
1034
|
|
|
999
1035
|
Because the winner is picked per field name, **answer a pair whole or not at
|
|
1000
1036
|
all**: a hook offering `MY_SIG` without `MY_SIG_INFO` takes the name and leaves
|
|
@@ -1113,7 +1149,7 @@ interface CommandHook {
|
|
|
1113
1149
|
// CommandCatalogEntry: {command, describe, params?, category?, needsOperation?, expectsParams?, suggest?}
|
|
1114
1150
|
// CommandAction (closed set): {setVfo?, setPower?, updateQso?, addQso?,
|
|
1115
1151
|
// setKeyerSpeed?, showKeyer?, setEnterSends?, sendSpots?, autoRespot?, syncAll?, updateSettings?, updateSetting?, openSettings?,
|
|
1116
|
-
// updateOperation?, markSegment?, setCallField?, reloadDataFiles?, devNotice?, devStatus?,
|
|
1152
|
+
// updateOperation?, markSegment?, findInLog?, setCallField?, setTime?, reloadDataFiles?, devNotice?, devStatus?,
|
|
1117
1153
|
// devProgress?, devHammer?, devSplash?, devOnboard?, devTester?, devPopup?,
|
|
1118
1154
|
// devTimeTravel?, toggleExperiment?}
|
|
1119
1155
|
```
|
package/docs/settings.md
CHANGED
|
@@ -79,13 +79,19 @@ export interface SimpleSettingsPanel {
|
|
|
79
79
|
// below — this only controls the top-level nav.
|
|
80
80
|
sidebar?: boolean;
|
|
81
81
|
// Defaults false. Set true to also show this panel as a row in the Data
|
|
82
|
-
// Files screen's own "Extensions" section —
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// sidebar-opt-out panel in here.
|
|
82
|
+
// Files screen's own "Extensions" section — leave this false for anything
|
|
83
|
+
// not actually about managing data files. Independent of `sidebar`; a
|
|
84
|
+
// panel usually wants both false-then-true, but the two aren't coupled at
|
|
85
|
+
// the type level — inferring this from `sidebar: false` alone would sweep
|
|
86
|
+
// any sidebar-opt-out panel in here.
|
|
88
87
|
dataFilesSection?: boolean;
|
|
88
|
+
// The key of one of the app's own settings sections ('spotting', the
|
|
89
|
+
// Spotting section) to show this panel inside, as one row that opens it
|
|
90
|
+
// in a dialog — instead of a section of its own in Settings and a sidebar
|
|
91
|
+
// entry, whatever `sidebar` says. An app without that section (an older
|
|
92
|
+
// one, or a key it doesn't know) shows the panel as if this weren't set,
|
|
93
|
+
// so it needs no newer API level.
|
|
94
|
+
section?: string;
|
|
89
95
|
fields: (SettingsField | FormActionElement)[];
|
|
90
96
|
}
|
|
91
97
|
```
|
|
@@ -136,6 +142,7 @@ export interface SettingsPanelDescriptor {
|
|
|
136
142
|
order?: number;
|
|
137
143
|
sidebar?: boolean; // see SimpleSettingsPanel.sidebar above; defaults true
|
|
138
144
|
dataFilesSection?: boolean; // see SimpleSettingsPanel.dataFilesSection above; defaults false
|
|
145
|
+
section?: string; // see SimpleSettingsPanel.section above
|
|
139
146
|
}
|
|
140
147
|
|
|
141
148
|
export interface DynamicSettingsPanel {
|
|
@@ -204,7 +211,14 @@ values). Tapping it opens an editor dialog with a row per item plus an
|
|
|
204
211
|
dialog. Editing or adding an item opens the same form renderer
|
|
205
212
|
recursively, scoped to `itemFields`, in a nested dialog — there is no
|
|
206
213
|
separate "list item editor" widget, just `FormRenderer` given a smaller
|
|
207
|
-
`FormDefinition`.
|
|
214
|
+
`FormDefinition`. An existing item is deleted from that dialog's footer,
|
|
215
|
+
once the operator confirms.
|
|
216
|
+
Saving it merges what it edited into the item, so keys it has no field
|
|
217
|
+
for are kept.
|
|
218
|
+
|
|
219
|
+
Set `itemSwitch` to the key of an on/off value in each item to give every
|
|
220
|
+
row a switch for it, and leave that key out of `itemFields`. An item without
|
|
221
|
+
it reads as on.
|
|
208
222
|
|
|
209
223
|
Set `orderable: true` to allow drag-to-reorder in the editor; omit it for
|
|
210
224
|
lists where order doesn't matter.
|