# Changelog

All notable changes to `@kvaser/canking-api` are documented in this file.

The SDK version follows the CanKing application version. Version increments are therefore tied to application releases
instead of strict SemVer bump rules based on breaking vs non-breaking API changes.

## [7.6.1] - 2026-10-08

### Non-breaking changes

#### `@kvaser/canking-api/controls`

- **Fixed:** `SelectSignalDialog` selected a whole message, and with it all of its signals, when a search
  filter was active and the user ticked the only signal of that message matching the search. The tree only
  knows the matching signals, so it took that one signal for all of them. A message is now selected only once
  all of its signals are selected.
- **Changed:** `SelectSignalDialog` in single-select mode labels its button "Close" instead of "Back" when
  the dialog is not shown full screen.
- **Fixed:** `CanKingDataProvider` could undo a pane change, such as a view the user had just picked for a
  pane, when project data was saved shortly after. A queued save was resolved against the pane list of an
  older render and wrote back every pane as it was then. It is now resolved against the panes last sent to or
  received from the host.

#### `@kvaser/canking-api/hooks`

- **Fixed:** `useProjectDataSlice` and `useProjectData` saved a slice by writing back the whole pane list,
  which also wrote back every other pane as the renderer last heard of it - undoing a pane change or a slice
  saved by another window that it had not heard of yet. Slices are now sent to the host on the
  `prj-update-workspace-pane-slices` channel, which applies them to its current panes. A CanKing version
  without that channel is detected on the first save and served through the pane list as before.
- **Fixed:** `useProjectDataSlice` reported the slice as not loaded for a moment every time the panes
  changed, which they do on every save of any slice by any view. A view only shown while its slice is loaded
  then unmounted and mounted again on every save, and changes made in between were dropped. The slice now
  stays loaded, and picks up a new value when the slice was changed elsewhere, such as from an extension
  dialog.
- **Fixed:** `useSignalData`, `useMessageData` and `useSignalDataEnvelopes` returned no data when a view
  subscribed to the same signals from its first render. The subscription was written before its project data
  slice had loaded, a write the slice setter drops, and was not written again. The same applied to the view
  width of `useSignalDataEnvelopes`. They are now also written once their slice has loaded.
- **Added:** `ISignalEnvelopePayload` - the type of the name-addressed envelopes the host sends to
  `useSignalDataEnvelopes`, now exported.

#### `@kvaser/canking-api/models`

- **Changed:** `electronApiFake`, used when there is no host, now rejects `prj-update-workspace-pane-slices`
  the way an older host does, so project data is saved through the pane list rather than dropped, and
  answers `mm-get-first-and-latest-timestamp` with zero timestamps.

### Dependencies

- Updated dependencies, among them Electron 44, React 19.3, `@mui/material` 7.3.11 and `@mui/x-tree-view`
  8.29. Extensions created with `@kvaser/create-canking-extension` get the matching versions.
- The build now uses Node.js 24.

## [7.6.0] - 2026-09-25

### Non-breaking changes

#### `@kvaser/canking-api/ipc`

- **Added:** `openExtensionDialog`, `closeExtensionDialog`, `getExtensionDialogArgs`,
  `ExtensionDialogOptions`, `ExtensionDialogResult` and `ExtensionDialogFailureReason` - an extension can now
  open one of its own routes in a separate CanKing window, so a dialog is no longer limited to the size of
  the extension's pane. The dialog is rendered from the same bundle at `/dialog/{dialogId}` and shares state
  with the pane through the workspace view id.

  Two things to know when using it:
  - `openExtensionDialog` resolves with `{ completed, result }`. Check `completed` before reading `result`:
    a CanKing version that predates this API reports `completed: false` with
    `reason: 'unsupported-host'`, so extensions should keep a fallback for that case.
  - Project data slices are kept in sync between the pane and the dialog, but session data slices are loaded
    once when a view mounts and are not pushed between windows. Use project data for state both need to see
    change.

#### `@kvaser/canking-api/controls`

- **Added:** `TextControlProps.onKeyDown` - new optional prop to handle the `onKeyDown` event.
- **Fixed:** `SelectSignalDialog` crashed the view with the tree view's "requires all items to have a unique
  id property" error when a database reported the same message twice, or a message the same signal twice -
  which a J1939 database has been seen to do for read-only signals, so showing the crash needed
  `showReadOnlySignals`. A repeated qualified name is now listed once instead.
- **Fixed:** `SelectSignalDialog` search was very slow with databases holding many messages and signals. Every
  keystroke re-scanned and re-lowercased all message and signal names several times, and expanded every
  matching message, which mounted a tree item per matching signal. Searching is now filtered from an index
  built once per database set, the search field no longer waits for the tree to re-filter, and a search that
  matches a very large number of signals lists the matching messages collapsed instead of expanding all of
  them. The tree is also built from a `RichTreeView` instead of a `SimpleTreeView`, so only the items the
  user has expanded are mounted rather than one per message and signal of the whole match set.
- **Fixed:** `SelectSignalDialog` left a message's checkbox unchanged when it was ticked while collapsed.
  A `SimpleTreeView` only knows the items currently mounted, so there were no signal items below a collapsed
  message for the selection to propagate down to. The `RichTreeView` it now uses is given the whole
  hierarchy as data, so ticking a message selects its signals whether or not they are on screen.

#### `@kvaser/canking-api/models`

- **Added:** `Frame.matchedMessageQualifiedName` - the qualified name of the database message a frame was
  decoded with, absent when it matched none. This is the only way to attribute a frame to the frame
  definition it came from: J1939 PGN lookup matches a database message whose identifier, priority and
  source-address bits differ from the frame on the bus, and message names are unique only within a single
  database rather than across a cluster.
- **Added:** `FixedPosFrameIdentifier.messageQualifiedName` - names the database message a subscription is
  for. When both the identifier and the frame carry a qualified name, matching compares that instead of the
  frame identifier and flags.
- **Added:** `calculateMessageSubscriptionKey`, `calculateMessageSubscriptionKeyFromFrame` and
  `frameMatchesSubscription`. Prefer `frameMatchesSubscription` over comparing
  `calculateSubscriptionKey` with `calculateSubscriptionKeyFromFrame` by hand: it matches on the frame
  identifier _or_ the database message identity, which an identifier-only comparison cannot do for a
  PGN-resolved message. `calculateSubscriptionKey` and `calculateSubscriptionKeyFromFrame` are unchanged.
- **Changed:** `compareFixedPosFrameIdentifiers` compares the message qualified name instead of the frame
  identifier and flags when both identifiers carry one. Behaviour is unchanged when either does not.
- **Added:** `IUserSettings.warnOnUnverifiedExtension` - whether installing an unsigned extension should warn
  first. Defaults to `true`; the warning dialog offers to turn it off, and the Settings dialog turns it back
  on. Code that builds an `IUserSettings` literal has to set the field; spread `defaultUserSettings` to pick
  up the default instead.

#### `@kvaser/canking-api/hooks`

- **Fixed:** `useSignalData` and `useSignalDataEnvelopes` returned no data for a signal in a J1939 database
  when the frame was resolved through PGN lookup. Both resolved the signal's qualified name to a frame
  definition and then matched incoming frames by that definition's identifier, which a PGN-resolved frame
  never carries. The message trace showed the decoded signals while the signal plot stayed empty.
- **Changed:** `useSignalDataEnvelopes` now also writes a `signalsubscription-<name>` project data slice and
  prefers name-addressed envelopes from the host, falling back to the frame-addressed payload when the host
  does not serve them. The `framesubscription-<name>` slice is still written, so an older host keeps serving
  the pane. No change is required in extension code.
- **Fixed:** `useLogMessages` returned messages whose `time` had none of `Long`'s methods, so
  `message.time.toNumber()` threw. Electron's Structured Clone Algorithm does not carry prototype chains, so
  the `Long` sent by CanKing arrived as a plain `{ low, high, unsigned }` object. It is now restored before
  the messages reach the store.
- **Fixed:** `useMeasurementStatus` and `useNodeStatus` returned a `dataProcessingStatus` whose
  `lastExecutionTime`, `minExecutionTime` and `maxExecutionTime` had none of `Long`'s methods, for the same
  reason. All three are now restored before the statuses reach the store.
- **Fixed:** `useSessionData` and `useSessionDataSlice` dropped the prototypes of values such as `Long`,
  which `useProjectData` and `useProjectDataSlice` preserve. Session data is now serialized in the renderer
  the same way project data is, so both hooks accept the same values: the encoder identifies a `Long` by a
  prototype flag that Electron's Structured Clone Algorithm strips, so a value handed to the host as-is can
  no longer be recognised as one by the time the host stores it.

  Both hooks now mark each session data call with `{ serialized: true }`, which is how CanKing tells them
  apart from an extension built against an older SDK - one that sends and expects values is served exactly
  as before, so no extension needs rebuilding. Requires CanKing 7.6.0 or later to take effect; an older
  host ignores the flag and answers with the value, which the hooks take as it is, behaving as they did
  before.

## [7.5.0] - 2026-05-29

### Breaking changes

#### `@kvaser/canking-api/controls`

- **Removed:** `useMessageDatabases` and `IMessageDatabase` were moved from`@kvaser/canking-api/controls` to `@kvaser/canking-api/hooks`.
- **Changed:** `ToolbarControl` no longer wraps items onto a new line when they overflow. Items that do not fit are now collapsed into an overflow menu (ellipsis button). Toolbars that relied on the previous multi-line wrapping behavior will need to adjust their layout.

#### `@kvaser/canking-api/hooks`

- **Changed:** `useOnlineStatus` changed signature and now returns `IOnlineStatusContext`. The hook now exposes `onlineStatus`, `isLoaded`, and `error` as one object instead of returning only the online status payload.
- **Added:** `useMessageDatabases` and `IMessageDatabase` were moved from`@kvaser/canking-api/controls` to `@kvaser/canking-api/hooks`.

#### `@kvaser/canking-api/models`

- **Removed:** `Frame.time` has been removed, use `Frame.timeUs` instead.
- **Removed:** `ApiPreferences` type and all related exports. API preferences (`version`, `language`,
  `useHexNumericBase`, `username`, `apiVersion`) are now conveyed via gRPC metadata and
  are no longer part of the proto message API.

### Non-breaking changes

#### `@kvaser/canking-api/controls`

- **Added:** `DividerItemProps` - new exported interface for configuring toolbar dividers.
- **Added:** `toolbarItem.divider` - new optional `divider` field on `toolbarItem` for inserting horizontal/vertical dividers into `ToolbarControl`.
- **Added:** `DropdownButtonProps.fullWidth` - new optional boolean prop to stretch a `DropdownButton` to its container width.
- **Added:** `ButtonProps.fullWidth` - new optional boolean prop on the toolbar's `ButtonProps` to stretch a button to available width.

#### `@kvaser/canking-api/hooks`

- **Added:** `useMeasurementSetupState`.
- **Added:** `useMeasurementStatusState`.
- **Added:** `useCombinedMeasurementTraceData`.

### Migration

- Update `useOnlineStatus` usage:

```tsx
// Before
const onlineStatus = useOnlineStatus();
const isOnline = onlineStatus?.isOnline === true;

// After
const { onlineStatus, isLoaded, error } = useOnlineStatus();
const isOnline = onlineStatus?.isOnline === true;
```

- Update `useMessageDatabases` and `IMessageDatabase` imports:

```tsx
// Before
import { useMessageDatabases, IMessageDatabase } from '@kvaser/canking-api/controls';

// After
import { useMessageDatabases, IMessageDatabase } from '@kvaser/canking-api/hooks';
```

- Use `Frame.timeUs` instead of `Frame.time`:

```tsx
// Before
const timeInSeconds = frame.time;

// After
const timeInSeconds = frame.timeUs.toNumber() / 1_000_000;
```

## [7.4.0] - 2026-02-27

- Initial public release of `@kvaser/canking-api`.
