@ham2k/extension-sdk 0.12.15 → 1.12.0
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 +7 -0
- package/dist/index.d.ts +14 -1
- package/dist/index.js +2 -0
- package/docs/hooks.md +8 -5
- package/package.json +1 -1
- package/samples/k2hrc-panafall/README.md +16 -3
- package/samples/k2hrc-panafall/src/panafall.ts +26 -6
package/README.md
CHANGED
|
@@ -53,6 +53,13 @@ offline and describe the version installed here rather than whatever the
|
|
|
53
53
|
repository says today. If an agent is writing your extension, point it at
|
|
54
54
|
`node_modules/@ham2k/extension-sdk/AGENTS.md` — `h2kext-init` does that for you.
|
|
55
55
|
|
|
56
|
+
## Versions
|
|
57
|
+
|
|
58
|
+
The minor version is the extension API: SDK 1.12 describes API 12, and
|
|
59
|
+
exports it as `API_VERSION`. `@ham2k/extension-tools` follows the same rule,
|
|
60
|
+
so a `^1.12.0` range says the extension needs API 12 and admits every later
|
|
61
|
+
one. A major version is a change that breaks an extension already written.
|
|
62
|
+
|
|
56
63
|
## Export types and settings
|
|
57
64
|
|
|
58
65
|
Export hooks can register shared types with `getExportTypes()`. Use
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ham2k/extension-sdk
|
|
1
|
+
// @ham2k/extension-sdk 1.12.0
|
|
2
2
|
/** Experimental v1 panel scene contract. All coordinates are in the scene's viewBox. */
|
|
3
3
|
export interface PanelScene {
|
|
4
4
|
version: 1;
|
|
@@ -1360,6 +1360,9 @@ export interface CommandAction {
|
|
|
1360
1360
|
findInLog?: {
|
|
1361
1361
|
query?: string;
|
|
1362
1362
|
};
|
|
1363
|
+
navigate?: {
|
|
1364
|
+
to: string;
|
|
1365
|
+
};
|
|
1363
1366
|
setCallField?: {
|
|
1364
1367
|
text: string;
|
|
1365
1368
|
};
|
|
@@ -1456,6 +1459,8 @@ export interface CommandCatalogEntry {
|
|
|
1456
1459
|
params?: string;
|
|
1457
1460
|
category?: string;
|
|
1458
1461
|
needsOperation?: boolean;
|
|
1462
|
+
needsTestData?: boolean;
|
|
1463
|
+
keywords?: string[];
|
|
1459
1464
|
expectsParams?: boolean;
|
|
1460
1465
|
suggest?: boolean;
|
|
1461
1466
|
}
|
|
@@ -2614,6 +2619,14 @@ export interface TemplateContextArgs {
|
|
|
2614
2619
|
}
|
|
2615
2620
|
export declare function templateContext(args: TemplateContextArgs): Record<string, unknown>;
|
|
2616
2621
|
export declare function isTestOperation(stationCall: JSONValue | undefined): boolean;
|
|
2622
|
+
/**
|
|
2623
|
+
* The extension API this SDK describes: the highest `api` a manifest written
|
|
2624
|
+
* against it can declare, and the one its types and host calls are for. The
|
|
2625
|
+
* SDK's and `@ham2k/extension-tools`' minor version is this number, so a
|
|
2626
|
+
* `^1.<api>.0` range names the oldest API an extension needs and admits every
|
|
2627
|
+
* later one.
|
|
2628
|
+
*/
|
|
2629
|
+
export declare const API_VERSION = 12;
|
|
2617
2630
|
export declare function defineExtension(def: ExtensionDefinition): void;
|
|
2618
2631
|
export declare const hooks: {
|
|
2619
2632
|
invokeAll(category: HookCategoryName, method: string, args: unknown, online?: boolean): Promise<{
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { base64ToBytes, bytesToBase64 } from "./base64.js";
|
|
2
2
|
import { adoptExtensionTimers, clearInterval, clearTimeout, setInterval, setTimeout } from "./timers.js";
|
|
3
|
+
const API_VERSION = 12;
|
|
3
4
|
export * from "./types.js";
|
|
4
5
|
export * from "./hookCategories.js";
|
|
5
6
|
export * from "./panelScene.js";
|
|
@@ -426,6 +427,7 @@ const host = {
|
|
|
426
427
|
}
|
|
427
428
|
};
|
|
428
429
|
export {
|
|
430
|
+
API_VERSION,
|
|
429
431
|
defineExtension,
|
|
430
432
|
hooks,
|
|
431
433
|
host
|
package/docs/hooks.md
CHANGED
|
@@ -1170,10 +1170,10 @@ interface CommandHook {
|
|
|
1170
1170
|
catalog?(ctx): Promise<CommandCatalogEntry[]>
|
|
1171
1171
|
}
|
|
1172
1172
|
// CommandInterpretation: {expectsParams?, mixedCase?, error?, describe?, confirm?, commands?: CommandAction[]}
|
|
1173
|
-
// CommandCatalogEntry: {command, describe, params?, category?, needsOperation?, expectsParams?, suggest?}
|
|
1173
|
+
// CommandCatalogEntry: {command, describe, params?, category?, needsOperation?, needsTestData?, keywords?, expectsParams?, suggest?}
|
|
1174
1174
|
// CommandAction (closed set): {setVfo?, setPower?, updateQso?, addQso?,
|
|
1175
1175
|
// setKeyerSpeed?, showKeyer?, setEnterSends?, sendSpots?, autoRespot?, syncAll?, updateSettings?, updateSetting?, openSettings?,
|
|
1176
|
-
// updateOperation?, markSegment?, findInLog?, setCallField?, setTime?, reloadDataFiles?, devNotice?, devStatus?,
|
|
1176
|
+
// updateOperation?, markSegment?, findInLog?, navigate?, setCallField?, setTime?, reloadDataFiles?, devNotice?, devStatus?,
|
|
1177
1177
|
// devProgress?, devHammer?, devSplash?, devOnboard?, devTester?, devPopup?,
|
|
1178
1178
|
// devTimeTravel?, toggleExperiment?}
|
|
1179
1179
|
```
|
|
@@ -1366,9 +1366,12 @@ Three things differ from `interpret`, all deliberate:
|
|
|
1366
1366
|
- **Called once and cached**, not per keystroke — dropped when the runtime is
|
|
1367
1367
|
replaced (an extension enabled/disabled) or the hook context is
|
|
1368
1368
|
invalidated (locale change, since `describe` is localized by the hook).
|
|
1369
|
-
- **Matching is host-side**,
|
|
1370
|
-
`command`
|
|
1371
|
-
|
|
1369
|
+
- **Matching is host-side**, case-insensitive, at the start of a word in
|
|
1370
|
+
`command` or in `keywords` — never mid-word, and `describe` is never
|
|
1371
|
+
searched. Every word that should find a command belongs in its
|
|
1372
|
+
`keywords`: the aliases it accepts when typed, and plain words for what it
|
|
1373
|
+
does. Same rule `interpret`'s own matching follows: the host never
|
|
1374
|
+
compiles extension-supplied patterns.
|
|
1372
1375
|
- **List commands, not variants.** `radio-commands` advertises one BAND
|
|
1373
1376
|
entry, not fifteen; `call-notes` advertises `//`, not every key the user
|
|
1374
1377
|
has defined.
|
package/package.json
CHANGED
|
@@ -4,9 +4,17 @@ The radio's panadapter and waterfall in a scene panel, with its span, reference
|
|
|
4
4
|
level and sweep speed, a local contrast control, and click-to-tune. The host
|
|
5
5
|
draws the sweeps in the `spectrum` control; they never reach the extension.
|
|
6
6
|
|
|
7
|
-
Needs a radio
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Needs a supported radio:
|
|
8
|
+
|
|
9
|
+
- Icom IC-705/7300/7610, at 115200 bps when using a serial line.
|
|
10
|
+
- FlexRadio, mirrored through a DAX IQ channel.
|
|
11
|
+
- Elecraft K4 over Ethernet, with a separate local TCP spectrum connection on
|
|
12
|
+
port 9201 alongside CAT. It displays the main panadapter with local zoom,
|
|
13
|
+
reference and speed controls. Levels are displayed as received, without an
|
|
14
|
+
RF calibration claim.
|
|
15
|
+
|
|
16
|
+
The panel shows only the controls each radio supports; contrast, averaging,
|
|
17
|
+
frame rate and waterfall rows remain local.
|
|
10
18
|
|
|
11
19
|
In this repository:
|
|
12
20
|
|
|
@@ -14,3 +22,8 @@ In this repository:
|
|
|
14
22
|
node build.mjs
|
|
15
23
|
node ../../tools/h2kext-pack.mjs build -o /tmp/k2hrc-panafall.h2kext
|
|
16
24
|
```
|
|
25
|
+
|
|
26
|
+
Samples are not included in the app's regular extension build. Install the
|
|
27
|
+
resulting `.h2kext` through **Settings → Extensions & Features → Install from
|
|
28
|
+
file…**, then add its **Panadapter** panel to an operation. The host must have
|
|
29
|
+
the matching radio support.
|
|
@@ -42,15 +42,25 @@ const spanLabel = (hz: number) => `±${hz >= 1000 ? `${hz / 1000} kHz` : `${hz}
|
|
|
42
42
|
export function scene(radio: PanelRadioState | null, display: Display, step = "100"): PanelScene {
|
|
43
43
|
const spectrum: PanelRadioSpectrum | null | undefined = radio?.spectrum
|
|
44
44
|
const knobs = spectrum && !spectrum.problem
|
|
45
|
+
const hasSpan = knobs && spectrum.spansHz.length > 0
|
|
46
|
+
const hasSpeed = knobs && spectrum.speeds.length > 0
|
|
47
|
+
const hasRef =
|
|
48
|
+
knobs &&
|
|
49
|
+
Number.isFinite(spectrum.refMinDb) &&
|
|
50
|
+
Number.isFinite(spectrum.refMaxDb) &&
|
|
51
|
+
Number.isFinite(spectrum.refStepDb) &&
|
|
52
|
+
spectrum.refMinDb < spectrum.refMaxDb &&
|
|
53
|
+
spectrum.refStepDb > 0
|
|
54
|
+
const ref = hasRef ? Math.max(spectrum.refMinDb, Math.min(spectrum.refMaxDb, spectrum.refLevelDb ?? spectrum.refMinDb)) : 0
|
|
45
55
|
const band = passband(radio?.mode)
|
|
46
56
|
return {
|
|
47
57
|
version: 1,
|
|
48
58
|
width: 600,
|
|
49
59
|
height: 24,
|
|
50
|
-
values: { floor: 0, average: 40, fps: 15, rows: 10, hoverHz: 0, hoverDb: 0, ref
|
|
60
|
+
values: { floor: 0, average: 40, fps: 15, rows: 10, hoverHz: 0, hoverDb: 0, ref },
|
|
51
61
|
strings: {
|
|
52
|
-
span: spectrum
|
|
53
|
-
speed: spectrum
|
|
62
|
+
span: hasSpan && spectrum.spanHz != null && spectrum.spansHz.includes(spectrum.spanHz) ? String(spectrum.spanHz) : "",
|
|
63
|
+
speed: hasSpeed && spectrum.speed != null && spectrum.speeds.includes(spectrum.speed) ? spectrum.speed : "",
|
|
54
64
|
display,
|
|
55
65
|
step,
|
|
56
66
|
},
|
|
@@ -65,7 +75,7 @@ export function scene(radio: PanelRadioState | null, display: Display, step = "1
|
|
|
65
75
|
},
|
|
66
76
|
],
|
|
67
77
|
controls: [
|
|
68
|
-
...(
|
|
78
|
+
...(hasSpan
|
|
69
79
|
? [
|
|
70
80
|
{
|
|
71
81
|
id: "span",
|
|
@@ -75,6 +85,10 @@ export function scene(radio: PanelRadioState | null, display: Display, step = "1
|
|
|
75
85
|
event: "span",
|
|
76
86
|
options: spectrum.spansHz.map((hz) => ({ label: spanLabel(hz), value: String(hz) })),
|
|
77
87
|
},
|
|
88
|
+
]
|
|
89
|
+
: []),
|
|
90
|
+
...(hasRef
|
|
91
|
+
? [
|
|
78
92
|
{
|
|
79
93
|
id: "ref",
|
|
80
94
|
label: "Reference level",
|
|
@@ -85,6 +99,10 @@ export function scene(radio: PanelRadioState | null, display: Display, step = "1
|
|
|
85
99
|
max: spectrum.refMaxDb,
|
|
86
100
|
step: spectrum.refStepDb,
|
|
87
101
|
},
|
|
102
|
+
]
|
|
103
|
+
: []),
|
|
104
|
+
...(hasSpeed
|
|
105
|
+
? [
|
|
88
106
|
{
|
|
89
107
|
id: "speed",
|
|
90
108
|
label: "Speed",
|
|
@@ -100,7 +118,7 @@ export function scene(radio: PanelRadioState | null, display: Display, step = "1
|
|
|
100
118
|
{ id: "fps", label: "Panadapter FPS", kind: "nativeSlider" as const, value: "fps", min: 1, max: 30, step: 1 },
|
|
101
119
|
{ id: "rows", label: "Waterfall rows per second", kind: "nativeSlider" as const, value: "rows", min: 1, max: 30, step: 1 },
|
|
102
120
|
// A control the layout does not place fails the scene: Ref's label goes with its slider.
|
|
103
|
-
...[...(
|
|
121
|
+
...[...(hasRef ? ["Ref"] : []), "Contrast", "Avg", "FPS", "Rows"].map((text) => ({
|
|
104
122
|
id: `label-${text}`,
|
|
105
123
|
label: text,
|
|
106
124
|
kind: "nativeText" as const,
|
|
@@ -179,7 +197,9 @@ export function scene(radio: PanelRadioState | null, display: Display, step = "1
|
|
|
179
197
|
column: [
|
|
180
198
|
{
|
|
181
199
|
wrap: [
|
|
182
|
-
...(
|
|
200
|
+
...(hasSpan ? [{ control: "span" }] : []),
|
|
201
|
+
...(hasRef ? [labeled("Ref", "ref")] : []),
|
|
202
|
+
...(hasSpeed ? [{ control: "speed" }] : []),
|
|
183
203
|
labeled("Contrast", "floor"),
|
|
184
204
|
labeled("Avg", "average"),
|
|
185
205
|
labeled("FPS", "fps"),
|