Add FeatureMap template helpers for static cluster config (#1714)

- Add if_cluster_has_feature_bitmap, if_feature_bit_enabled, and cluster_feature_items helpers for Feature bitmap detection and per-bit checks in generated server cluster config templates.
- Fix Electron 41 navigation and console-message handling, use navigationHistory for back/forward when available and support the new console-message event shape; allow Electron 41.x.x in env checks.

- Adding unit tests
- JIRA: ZAPP-1716
diff --git a/docs/api.md b/docs/api.md
index b6ff0da..c169bf4 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -9196,6 +9196,10 @@
     * [~featureBits(options)](#module_Templating API_ Attribute helpers..featureBits) ⇒
     * [~attributeDefault()](#module_Templating API_ Attribute helpers..attributeDefault) ⇒
     * [~as_underlying_atomic_identifier_for_attribute_id(attributeId)](#module_Templating API_ Attribute helpers..as_underlying_atomic_identifier_for_attribute_id)
+    * [~selectFeatureBitmapForCluster(db, packageIds, clusterId)](#module_Templating API_ Attribute helpers..selectFeatureBitmapForCluster) ⇒ <code>Promise.&lt;(object\|null)&gt;</code>
+    * [~if_cluster_has_feature_bitmap(options)](#module_Templating API_ Attribute helpers..if_cluster_has_feature_bitmap) ⇒ <code>Promise.&lt;string&gt;</code>
+    * [~if_feature_bit_enabled(options)](#module_Templating API_ Attribute helpers..if_feature_bit_enabled) ⇒ <code>string</code>
+    * [~cluster_feature_items(options)](#module_Templating API_ Attribute helpers..cluster_feature_items) ⇒ <code>Promise.&lt;string&gt;</code>
 
 <a name="module_Templating API_ Attribute helpers..count_mandatory_matter_attributes"></a>
 
@@ -9236,6 +9240,139 @@
 | --- | --- |
 | attributeId | <code>\*</code> | 
 
+<a name="module_Templating API_ Attribute helpers..selectFeatureBitmapForCluster"></a>
+
+### Templating API: Attribute helpers~selectFeatureBitmapForCluster(db, packageIds, clusterId) ⇒ <code>Promise.&lt;(object\|null)&gt;</code>
+Returns the cluster-scoped 'Feature' bitmap, or null when the cluster does not
+define one. Uses selectBitmapByNameAndClusterId for a targeted lookup, then
+confirms the bitmap is associated with clusterId (the DB helper may return a
+package-wide singleton when only one bitmap with that name exists).
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+
+| Param | Type |
+| --- | --- |
+| db | <code>\*</code> | 
+| packageIds | <code>Array.&lt;number&gt;</code> | 
+| clusterId | <code>number</code> | 
+
+<a name="module_Templating API_ Attribute helpers..if_cluster_has_feature_bitmap"></a>
+
+### Templating API: Attribute helpers~if\_cluster\_has\_feature\_bitmap(options) ⇒ <code>Promise.&lt;string&gt;</code>
+Block helper that renders its body when the cluster in the current context
+defines a bitmap named 'Feature', and its inverse ({{else}}) when it does
+not. Intended for choosing between `using FeatureBitmapType = Feature;` and
+`using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;`
+in static-cluster-config headers.
+
+Must be used inside a context that exposes a cluster `id` field, such as
+`zcl_clusters`, `selectedServerCluster`, or any block helper whose context
+object carries the ZCL cluster database ID as `id`.
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>Promise.&lt;string&gt;</code> - Rendered `fn` block when the cluster has a
+  bitmap named 'Feature'; rendered `inverse` block otherwise.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object (fn / inverse blocks). |
+
+**Example**  
+```js
+// Inside a selectedServerCluster or zcl_clusters block:
+{{#if_cluster_has_feature_bitmap}}
+using FeatureBitmapType = Feature;
+{{else}}
+using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;
+{{/if_cluster_has_feature_bitmap}}
+```
+<a name="module_Templating API_ Attribute helpers..if_feature_bit_enabled"></a>
+
+### Templating API: Attribute helpers~if\_feature\_bit\_enabled(options) ⇒ <code>string</code>
+Block helper that renders its body when the bitwise AND of `featureMapValue`
+and `mask` is non-zero (i.e. the specific feature bit is set), and its
+inverse ({{else}}) when the bit is clear.
+
+Both arguments are parsed as integers so decimal strings (e.g. `"3"`) and
+hex strings (e.g. `"0x3"`) are accepted.
+
+Combine with `cluster_feature_items` when you want to iterate over feature
+fields and selectively render only those that are enabled:
+{{#cluster_feature_items clusterCode}}
+  {{#if_feature_bit_enabled featureMapValue mask}}...{{/if_feature_bit_enabled}}
+{{/cluster_feature_items}}
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>string</code> - Rendered `fn` block when `(featureMapValue & mask) !== 0`;
+  rendered `inverse` block otherwise.  
+**Given**: <code>string\|number</code> featureMapValue - The raw FeatureMap attribute default
+  value for the current endpoint cluster (e.g. `"3"`).  
+**Given**: <code>string\|number</code> mask - The bitmask of the feature field being tested
+  (e.g. `1`).  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object (fn / inverse blocks). |
+
+**Example**  
+```js
+// LevelControl, featureMap default = 3 (kOnOff | kLighting):
+{{#if_feature_bit_enabled "3" "1"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+// → "enabled"
+{{#if_feature_bit_enabled "3" "4"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+// → "disabled"  (kFrequency bit 0x4 is not set)
+```
+<a name="module_Templating API_ Attribute helpers..cluster_feature_items"></a>
+
+### Templating API: Attribute helpers~cluster\_feature\_items(options) ⇒ <code>Promise.&lt;string&gt;</code>
+Block helper that iterates over all fields of the Feature bitmap for a
+cluster identified by its ZCL cluster code, regardless of which bits are
+currently enabled. Use `if_feature_bit_enabled` inside the body to act on
+only those fields whose bit is set in a given FeatureMap value.
+
+This is the preferred way to access Feature bitmap fields inside
+`user_cluster_attributes` because `zcl_bitmaps` requires a cluster database
+`id` in context that is not available there. This helper performs the cluster
+lookup by ZCL code (not name) for robustness.
+
+Each iteration context exposes:
+  - `name`  {string}  field name as stored in the ZCL database (e.g. `"kOnOff"`)
+  - `label` {string}  same as `name`
+  - `mask`  {number}  the field's bitmask (e.g. `1`)
+
+If the cluster has no bitmap named 'Feature' the body is never rendered.
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>Promise.&lt;string&gt;</code> - Concatenated rendered blocks for each feature field.  
+**Given**: <code>string\|number</code> clusterCode - The ZCL cluster code (e.g. `8` for
+  LevelControl). Inside `user_cluster_attributes` pass `../code` to
+  reference the enclosing `user_clusters` cluster code.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object. |
+
+**Example**  
+```js
+// List only enabled feature bits for LevelControl (featureMap default = 3):
+{{#if (is_str_equal name "FeatureMap")}}
+{{#cluster_feature_items ../code}}
+{{#if_feature_bit_enabled ../defaultValue mask}}
+    FeatureBitmapType::k{{asUpperCamelCase label preserveAcronyms=true}}, // feature bit {{as_hex mask}}
+{{/if_feature_bit_enabled}}
+{{/cluster_feature_items}}
+{{/if}}
+// Emits: FeatureBitmapType::kOnOff,     // feature bit 0x1
+//        FeatureBitmapType::kLighting,   // feature bit 0x2
+// Skips kFrequency (mask 0x4 not set in value 3)
+```
+**Example**  
+```js
+// List ALL defined feature bits regardless of enabled state:
+{{#cluster_feature_items clusterCode}}
+    {{label}}: {{as_hex mask}}
+{{/cluster_feature_items}}
+```
 <a name="module_Templating API_ C formatting helpers"></a>
 
 ## Templating API: C formatting helpers
diff --git a/docs/helpers.md b/docs/helpers.md
index 9b47035..7a1aad7 100644
--- a/docs/helpers.md
+++ b/docs/helpers.md
@@ -221,6 +221,10 @@
     * [~featureBits(options)](#module_Templating API_ Attribute helpers..featureBits) ⇒
     * [~attributeDefault()](#module_Templating API_ Attribute helpers..attributeDefault) ⇒
     * [~as_underlying_atomic_identifier_for_attribute_id(attributeId)](#module_Templating API_ Attribute helpers..as_underlying_atomic_identifier_for_attribute_id)
+    * [~selectFeatureBitmapForCluster(db, packageIds, clusterId)](#module_Templating API_ Attribute helpers..selectFeatureBitmapForCluster) ⇒ <code>Promise.&lt;(object\|null)&gt;</code>
+    * [~if_cluster_has_feature_bitmap(options)](#module_Templating API_ Attribute helpers..if_cluster_has_feature_bitmap) ⇒ <code>Promise.&lt;string&gt;</code>
+    * [~if_feature_bit_enabled(options)](#module_Templating API_ Attribute helpers..if_feature_bit_enabled) ⇒ <code>string</code>
+    * [~cluster_feature_items(options)](#module_Templating API_ Attribute helpers..cluster_feature_items) ⇒ <code>Promise.&lt;string&gt;</code>
 
 <a name="module_Templating API_ Attribute helpers..count_mandatory_matter_attributes"></a>
 
@@ -261,6 +265,139 @@
 | --- | --- |
 | attributeId | <code>\*</code> | 
 
+<a name="module_Templating API_ Attribute helpers..selectFeatureBitmapForCluster"></a>
+
+### Templating API: Attribute helpers~selectFeatureBitmapForCluster(db, packageIds, clusterId) ⇒ <code>Promise.&lt;(object\|null)&gt;</code>
+Returns the cluster-scoped 'Feature' bitmap, or null when the cluster does not
+define one. Uses selectBitmapByNameAndClusterId for a targeted lookup, then
+confirms the bitmap is associated with clusterId (the DB helper may return a
+package-wide singleton when only one bitmap with that name exists).
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+
+| Param | Type |
+| --- | --- |
+| db | <code>\*</code> | 
+| packageIds | <code>Array.&lt;number&gt;</code> | 
+| clusterId | <code>number</code> | 
+
+<a name="module_Templating API_ Attribute helpers..if_cluster_has_feature_bitmap"></a>
+
+### Templating API: Attribute helpers~if\_cluster\_has\_feature\_bitmap(options) ⇒ <code>Promise.&lt;string&gt;</code>
+Block helper that renders its body when the cluster in the current context
+defines a bitmap named 'Feature', and its inverse ({{else}}) when it does
+not. Intended for choosing between `using FeatureBitmapType = Feature;` and
+`using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;`
+in static-cluster-config headers.
+
+Must be used inside a context that exposes a cluster `id` field, such as
+`zcl_clusters`, `selectedServerCluster`, or any block helper whose context
+object carries the ZCL cluster database ID as `id`.
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>Promise.&lt;string&gt;</code> - Rendered `fn` block when the cluster has a
+  bitmap named 'Feature'; rendered `inverse` block otherwise.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object (fn / inverse blocks). |
+
+**Example**  
+```js
+// Inside a selectedServerCluster or zcl_clusters block:
+{{#if_cluster_has_feature_bitmap}}
+using FeatureBitmapType = Feature;
+{{else}}
+using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;
+{{/if_cluster_has_feature_bitmap}}
+```
+<a name="module_Templating API_ Attribute helpers..if_feature_bit_enabled"></a>
+
+### Templating API: Attribute helpers~if\_feature\_bit\_enabled(options) ⇒ <code>string</code>
+Block helper that renders its body when the bitwise AND of `featureMapValue`
+and `mask` is non-zero (i.e. the specific feature bit is set), and its
+inverse ({{else}}) when the bit is clear.
+
+Both arguments are parsed as integers so decimal strings (e.g. `"3"`) and
+hex strings (e.g. `"0x3"`) are accepted.
+
+Combine with `cluster_feature_items` when you want to iterate over feature
+fields and selectively render only those that are enabled:
+{{#cluster_feature_items clusterCode}}
+  {{#if_feature_bit_enabled featureMapValue mask}}...{{/if_feature_bit_enabled}}
+{{/cluster_feature_items}}
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>string</code> - Rendered `fn` block when `(featureMapValue & mask) !== 0`;
+  rendered `inverse` block otherwise.  
+**Given**: <code>string\|number</code> featureMapValue - The raw FeatureMap attribute default
+  value for the current endpoint cluster (e.g. `"3"`).  
+**Given**: <code>string\|number</code> mask - The bitmask of the feature field being tested
+  (e.g. `1`).  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object (fn / inverse blocks). |
+
+**Example**  
+```js
+// LevelControl, featureMap default = 3 (kOnOff | kLighting):
+{{#if_feature_bit_enabled "3" "1"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+// → "enabled"
+{{#if_feature_bit_enabled "3" "4"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+// → "disabled"  (kFrequency bit 0x4 is not set)
+```
+<a name="module_Templating API_ Attribute helpers..cluster_feature_items"></a>
+
+### Templating API: Attribute helpers~cluster\_feature\_items(options) ⇒ <code>Promise.&lt;string&gt;</code>
+Block helper that iterates over all fields of the Feature bitmap for a
+cluster identified by its ZCL cluster code, regardless of which bits are
+currently enabled. Use `if_feature_bit_enabled` inside the body to act on
+only those fields whose bit is set in a given FeatureMap value.
+
+This is the preferred way to access Feature bitmap fields inside
+`user_cluster_attributes` because `zcl_bitmaps` requires a cluster database
+`id` in context that is not available there. This helper performs the cluster
+lookup by ZCL code (not name) for robustness.
+
+Each iteration context exposes:
+  - `name`  {string}  field name as stored in the ZCL database (e.g. `"kOnOff"`)
+  - `label` {string}  same as `name`
+  - `mask`  {number}  the field's bitmask (e.g. `1`)
+
+If the cluster has no bitmap named 'Feature' the body is never rendered.
+
+**Kind**: inner method of [<code>Templating API: Attribute helpers</code>](#module_Templating API_ Attribute helpers)  
+**Returns**: <code>Promise.&lt;string&gt;</code> - Concatenated rendered blocks for each feature field.  
+**Given**: <code>string\|number</code> clusterCode - The ZCL cluster code (e.g. `8` for
+  LevelControl). Inside `user_cluster_attributes` pass `../code` to
+  reference the enclosing `user_clusters` cluster code.  
+
+| Param | Type | Description |
+| --- | --- | --- |
+| options | <code>\*</code> | Handlebars options object. |
+
+**Example**  
+```js
+// List only enabled feature bits for LevelControl (featureMap default = 3):
+{{#if (is_str_equal name "FeatureMap")}}
+{{#cluster_feature_items ../code}}
+{{#if_feature_bit_enabled ../defaultValue mask}}
+    FeatureBitmapType::k{{asUpperCamelCase label preserveAcronyms=true}}, // feature bit {{as_hex mask}}
+{{/if_feature_bit_enabled}}
+{{/cluster_feature_items}}
+{{/if}}
+// Emits: FeatureBitmapType::kOnOff,     // feature bit 0x1
+//        FeatureBitmapType::kLighting,   // feature bit 0x2
+// Skips kFrequency (mask 0x4 not set in value 3)
+```
+**Example**  
+```js
+// List ALL defined feature bits regardless of enabled state:
+{{#cluster_feature_items clusterCode}}
+    {{label}}: {{as_hex mask}}
+{{/cluster_feature_items}}
+```
 <a name="module_Templating API_ C formatting helpers"></a>
 
 ## Templating API: C formatting helpers
diff --git a/src-electron/generator/helper-attribute.js b/src-electron/generator/helper-attribute.js
index 613ca92..0ce7109 100644
--- a/src-electron/generator/helper-attribute.js
+++ b/src-electron/generator/helper-attribute.js
@@ -205,6 +205,190 @@
   }
 }
 
+/**
+ * Returns the cluster-scoped 'Feature' bitmap, or null when the cluster does not
+ * define one. Uses selectBitmapByNameAndClusterId for a targeted lookup, then
+ * confirms the bitmap is associated with clusterId (the DB helper may return a
+ * package-wide singleton when only one bitmap with that name exists).
+ *
+ * @param {*} db
+ * @param {number[]} packageIds
+ * @param {number} clusterId
+ * @returns {Promise<object|null>}
+ */
+async function selectFeatureBitmapForCluster(db, packageIds, clusterId) {
+  const candidate = await queryZcl.selectBitmapByNameAndClusterId(
+    db,
+    'Feature',
+    clusterId,
+    packageIds
+  )
+  if (!candidate) {
+    return null
+  }
+  const clusterBitmaps = (
+    await Promise.all(
+      packageIds.map((pkgId) =>
+        queryZcl.selectClusterBitmaps(db, pkgId, clusterId)
+      )
+    )
+  ).flat()
+  return clusterBitmaps.some((b) => b.id === candidate.id) ? candidate : null
+}
+
+/**
+ * Block helper that renders its body when the cluster in the current context
+ * defines a bitmap named 'Feature', and its inverse ({{else}}) when it does
+ * not. Intended for choosing between `using FeatureBitmapType = Feature;` and
+ * `using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;`
+ * in static-cluster-config headers.
+ *
+ * Must be used inside a context that exposes a cluster `id` field, such as
+ * `zcl_clusters`, `selectedServerCluster`, or any block helper whose context
+ * object carries the ZCL cluster database ID as `id`.
+ *
+ * @param {*} options - Handlebars options object (fn / inverse blocks).
+ * @returns {Promise<string>} Rendered `fn` block when the cluster has a
+ *   bitmap named 'Feature'; rendered `inverse` block otherwise.
+ *
+ * @example
+ * // Inside a selectedServerCluster or zcl_clusters block:
+ * {{#if_cluster_has_feature_bitmap}}
+ * using FeatureBitmapType = Feature;
+ * {{else}}
+ * using FeatureBitmapType = Clusters::StaticApplicationConfig::NoFeatureFlagsDefined;
+ * {{/if_cluster_has_feature_bitmap}}
+ */
+async function if_cluster_has_feature_bitmap(options) {
+  if (!('id' in this))
+    throw new Error(
+      'if_cluster_has_feature_bitmap requires a cluster id inside the context.'
+    )
+  const packageIds = await templateUtil.ensureZclPackageIds(this)
+  const featureBitmap = await selectFeatureBitmapForCluster(
+    this.global.db,
+    packageIds,
+    this.id
+  )
+  return featureBitmap ? options.fn(this) : options.inverse(this)
+}
+
+/**
+ * Block helper that renders its body when the bitwise AND of `featureMapValue`
+ * and `mask` is non-zero (i.e. the specific feature bit is set), and its
+ * inverse ({{else}}) when the bit is clear.
+ *
+ * Both arguments are parsed as integers so decimal strings (e.g. `"3"`) and
+ * hex strings (e.g. `"0x3"`) are accepted.
+ *
+ * Combine with `cluster_feature_items` when you want to iterate over feature
+ * fields and selectively render only those that are enabled:
+ * {{#cluster_feature_items clusterCode}}
+ *   {{#if_feature_bit_enabled featureMapValue mask}}...{{/if_feature_bit_enabled}}
+ * {{/cluster_feature_items}}
+ *
+ * @given {string|number} featureMapValue - The raw FeatureMap attribute default
+ *   value for the current endpoint cluster (e.g. `"3"`).
+ * @given {string|number} mask - The bitmask of the feature field being tested
+ *   (e.g. `1`).
+ * @param {*} options - Handlebars options object (fn / inverse blocks).
+ * @returns {string} Rendered `fn` block when `(featureMapValue & mask) !== 0`;
+ *   rendered `inverse` block otherwise.
+ *
+ * @example
+ * // LevelControl, featureMap default = 3 (kOnOff | kLighting):
+ * {{#if_feature_bit_enabled "3" "1"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+ * // → "enabled"
+ * {{#if_feature_bit_enabled "3" "4"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+ * // → "disabled"  (kFrequency bit 0x4 is not set)
+ */
+function if_feature_bit_enabled(featureMapValue, mask, options) {
+  const value = parseInt(featureMapValue)
+  return !isNaN(value) && (value & parseInt(mask)) !== 0
+    ? options.fn(this)
+    : options.inverse(this)
+}
+
+/**
+ * Block helper that iterates over all fields of the Feature bitmap for a
+ * cluster identified by its ZCL cluster code, regardless of which bits are
+ * currently enabled. Use `if_feature_bit_enabled` inside the body to act on
+ * only those fields whose bit is set in a given FeatureMap value.
+ *
+ * This is the preferred way to access Feature bitmap fields inside
+ * `user_cluster_attributes` because `zcl_bitmaps` requires a cluster database
+ * `id` in context that is not available there. This helper performs the cluster
+ * lookup by ZCL code (not name) for robustness.
+ *
+ * Each iteration context exposes:
+ *   - `name`  {string}  field name as stored in the ZCL database (e.g. `"kOnOff"`)
+ *   - `label` {string}  same as `name`
+ *   - `mask`  {number}  the field's bitmask (e.g. `1`)
+ *
+ * If the cluster has no bitmap named 'Feature' the body is never rendered.
+ *
+ * @given {string|number} clusterCode - The ZCL cluster code (e.g. `8` for
+ *   LevelControl). Inside `user_cluster_attributes` pass `../code` to
+ *   reference the enclosing `user_clusters` cluster code.
+ * @param {*} options - Handlebars options object.
+ * @returns {Promise<string>} Concatenated rendered blocks for each feature field.
+ *
+ * @example
+ * // List only enabled feature bits for LevelControl (featureMap default = 3):
+ * {{#if (is_str_equal name "FeatureMap")}}
+ * {{#cluster_feature_items ../code}}
+ * {{#if_feature_bit_enabled ../defaultValue mask}}
+ *     FeatureBitmapType::k{{asUpperCamelCase label preserveAcronyms=true}}, // feature bit {{as_hex mask}}
+ * {{/if_feature_bit_enabled}}
+ * {{/cluster_feature_items}}
+ * {{/if}}
+ * // Emits: FeatureBitmapType::kOnOff,     // feature bit 0x1
+ * //        FeatureBitmapType::kLighting,   // feature bit 0x2
+ * // Skips kFrequency (mask 0x4 not set in value 3)
+ *
+ * @example
+ * // List ALL defined feature bits regardless of enabled state:
+ * {{#cluster_feature_items clusterCode}}
+ *     {{label}}: {{as_hex mask}}
+ * {{/cluster_feature_items}}
+ */
+async function cluster_feature_items(clusterCode, options) {
+  const packageIds = await templateUtil.ensureZclPackageIds(this)
+
+  // Resolve the internal cluster DB ID from the ZCL cluster code.
+  let clusterId = null
+  for (const pkgId of packageIds) {
+    const cluster = await queryZcl.selectClusterByCode(
+      this.global.db,
+      pkgId,
+      parseInt(clusterCode)
+    )
+    if (cluster) {
+      clusterId = cluster.id
+      break
+    }
+  }
+  if (clusterId == null) {
+    return ''
+  }
+
+  const featureBitmap = await selectFeatureBitmapForCluster(
+    this.global.db,
+    packageIds,
+    clusterId
+  )
+  if (!featureBitmap) {
+    return ''
+  }
+
+  const allFields = await queryZcl.selectAllBitmapFieldsById(
+    this.global.db,
+    featureBitmap.id
+  )
+  const p = templateUtil.collectBlocks(allFields, options, this)
+  return templateUtil.templatePromise(this.global, p)
+}
+
 // WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
 //
 // Note: these exports are public API. Templates that might have been created in the past and are
@@ -216,3 +400,6 @@
 exports.as_underlying_atomic_identifier_for_attribute_id =
   as_underlying_atomic_identifier_for_attribute_id
 exports.count_mandatory_matter_attributes = count_mandatory_matter_attributes
+exports.if_cluster_has_feature_bitmap = if_cluster_has_feature_bitmap
+exports.if_feature_bit_enabled = if_feature_bit_enabled
+exports.cluster_feature_items = cluster_feature_items
diff --git a/src-electron/ui/menu.js b/src-electron/ui/menu.js
index afac220..1ec2c9f 100644
--- a/src-electron/ui/menu.js
+++ b/src-electron/ui/menu.js
@@ -108,13 +108,23 @@
       {
         label: 'Navigate back ...',
         click(menuItem, browserWindow, event) {
-          browserWindow.webContents.goBack()
+          const nav = browserWindow.webContents.navigationHistory
+          if (nav) {
+            nav.goBack()
+          } else {
+            browserWindow.webContents.goBack()
+          }
         }
       },
       {
         label: 'Navigate forward ...',
         click(menuItem, browserWindow, event) {
-          browserWindow.webContents.goForward()
+          const nav = browserWindow.webContents.navigationHistory
+          if (nav) {
+            nav.goForward()
+          } else {
+            browserWindow.webContents.goForward()
+          }
         }
       },
       { role: 'reload' },
diff --git a/src-electron/ui/window.js b/src-electron/ui/window.js
index b017b33..994958e 100644
--- a/src-electron/ui/window.js
+++ b/src-electron/ui/window.js
@@ -187,14 +187,15 @@
     }
   }) // EO close
 
-  w.webContents.on(
-    'console-message',
-    (event, level, message, line, sourceId) => {
-      if (!browserApi.processRendererNotify(w, message)) {
-        env.logBrowser(message)
-      }
+  w.webContents.on('console-message', (event, ...legacyArgs) => {
+    // Electron 41+ passes a single WebContentsConsoleMessageEventParams object;
+    // older versions passed (level, message, line, sourceId) positional args.
+    const message =
+      typeof event.message === 'string' ? event.message : (legacyArgs[1] ?? '')
+    if (!browserApi.processRendererNotify(w, message)) {
+      env.logBrowser(message)
     }
-  )
+  })
   w.webContents.on('before-input-event', (e, input) => {
     if (input.type === 'keyUp' && input.key.toLowerCase() === 'alt') {
       menu.toggleMenu(port)
diff --git a/src-electron/util/env.js b/src-electron/util/env.js
index 7a87bcd..5690303 100644
--- a/src-electron/util/env.js
+++ b/src-electron/util/env.js
@@ -596,7 +596,8 @@
     '18.x.x',
     '24.x.x',
     '27.x.x',
-    '31.x.x'
+    '31.x.x',
+    '41.x.x'
   ]
   let nodeVersion = process.version
   let electronVersion = process.versions.electron
diff --git a/test/gen-matter-1.test.js b/test/gen-matter-1.test.js
index bc43450..867d3fd 100644
--- a/test/gen-matter-1.test.js
+++ b/test/gen-matter-1.test.js
@@ -324,6 +324,49 @@
       '> Field: 0 SoftwareVersion  default_value=0x00000000'
     )
     expect(eventOut).not.toContain('> Field: ProductID  default_value=')
+
+    // Testing the FeatureMap template helpers added in helper-attribute.js:
+    //   if_cluster_has_feature_bitmap, cluster_feature_items, if_feature_bit_enabled
+    // The feature-map.zapt template exercises these helpers; assertions below
+    // verify that the generated feature-map.h reflects the Feature bitmap
+    // information loaded from cluster XMLs (e.g. level-control-cluster.xml and
+    // color-control-cluster.xml).
+    let featureMap = genResult.content['feature-map.h']
+    expect(featureMap).not.toBeNull()
+
+    // if_cluster_has_feature_bitmap: clusters defining <features> in their XML
+    // (Level Control / Color Control) must render the affirmative branch.
+    expect(featureMap).toContain('- Level Control has Feature bitmap')
+    expect(featureMap).toContain('- Color Control has Feature bitmap')
+    // A cluster with no Feature bitmap defined must render the {{else}} branch.
+    expect(featureMap).toContain('- Descriptor has no Feature bitmap')
+
+    // cluster_feature_items: iterates all fields of the Feature bitmap for the
+    // given cluster code. Level Control (0x0008) defines OO/LT/FQ feature bits;
+    // Color Control (0x0300) defines HS/EHUE/CL/XY/CT feature bits. The helper
+    // exposes each field's name as `label` and bit-position as `mask`.
+    expect(featureMap).toContain('LevelControl feature: OnOff mask=0x01')
+    expect(featureMap).toContain('LevelControl feature: Lighting mask=0x02')
+    expect(featureMap).toContain('LevelControl feature: Frequency mask=0x04')
+    expect(featureMap).toContain(
+      'ColorControl feature: Hue/Saturation mask=0x01'
+    )
+    expect(featureMap).toContain('ColorControl feature: Enhanced Hue mask=0x02')
+    expect(featureMap).toContain('ColorControl feature: Color loop mask=0x04')
+    expect(featureMap).toContain('ColorControl feature: XY mask=0x08')
+    expect(featureMap).toContain(
+      'ColorControl feature: Color temperature mask=0x10'
+    )
+    // Unknown cluster code: helper renders an empty inner block.
+    expect(featureMap).not.toContain('UnknownCluster feature:')
+
+    // if_feature_bit_enabled: bitwise-AND check accepting both decimal and hex
+    // string inputs; non-numeric input renders the {{else}} branch.
+    expect(featureMap).toContain('- value=3 mask=1 => enabled')
+    expect(featureMap).toContain('- value=3 mask=2 => enabled')
+    expect(featureMap).toContain('- value=3 mask=4 => disabled')
+    expect(featureMap).toContain('- value=0x1F mask=0x10 => enabled')
+    expect(featureMap).toContain('- value=notanumber mask=1 => disabled')
   },
   testUtil.timeout.long()
 )
diff --git a/test/gen-template/matter/feature-map.zapt b/test/gen-template/matter/feature-map.zapt
index 8c1f317..db83a84 100644
--- a/test/gen-template/matter/feature-map.zapt
+++ b/test/gen-template/matter/feature-map.zapt
@@ -9,4 +9,30 @@
     {{index}}: Bit {{bit}} is assigned to tag {{tag}} => value = {{value}}
 {{/feature_bits}}
 {{/global_attribute_default}}
-{{/zcl_clusters}}
\ No newline at end of file
+{{#if_cluster_has_feature_bitmap}}
+- {{label}} has Feature bitmap
+{{else}}
+- {{label}} has no Feature bitmap
+{{/if_cluster_has_feature_bitmap}}
+{{/zcl_clusters}}
+
+// cluster_feature_items helper:
+// Level Control (code 0x0008) -- features defined inline in level-control-cluster.xml:
+{{#cluster_feature_items 8}}
+    LevelControl feature: {{label}} mask={{asHex mask 2}}
+{{/cluster_feature_items}}
+// Color Control (code 0x0300) -- features defined inline in color-control-cluster.xml:
+{{#cluster_feature_items 768}}
+    ColorControl feature: {{label}} mask={{asHex mask 2}}
+{{/cluster_feature_items}}
+// Unknown cluster code (no output expected, no Feature bitmap defined):
+{{#cluster_feature_items 65535}}
+    UnknownCluster feature: {{label}} mask={{asHex mask 2}}
+{{/cluster_feature_items}}
+
+// if_feature_bit_enabled helper:
+- value=3 mask=1 => {{#if_feature_bit_enabled "3" "1"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+- value=3 mask=2 => {{#if_feature_bit_enabled "3" "2"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+- value=3 mask=4 => {{#if_feature_bit_enabled "3" "4"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+- value=0x1F mask=0x10 => {{#if_feature_bit_enabled "0x1F" "0x10"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
+- value=notanumber mask=1 => {{#if_feature_bit_enabled "notanumber" "1"}}enabled{{else}}disabled{{/if_feature_bit_enabled}}
\ No newline at end of file