diff --git a/go/jsruntime/uikit/CrmTabs.tsx b/go/jsruntime/uikit/CrmTabs.tsx
index 1659c42e..d058ad40 100644
--- a/go/jsruntime/uikit/CrmTabs.tsx
+++ b/go/jsruntime/uikit/CrmTabs.tsx
@@ -1,4 +1,4 @@
-import { createSignal, onCleanup, onMount, Show, For, JSXElement } from "solid-js";
+import { createSignal, createMemo, onCleanup, onMount, Show, For, JSXElement } from "solid-js";
// CrmTabGroup / CrmSubTabGroup — drop-in, behaviourally identical siblings of
// TabGroup (same props, storageKey syncing, controlled/uncontrolled index) with
@@ -26,13 +26,15 @@ function resolveCrmBadge(badge: number | undefined): number | undefined {
// Shared active-index state: mirrors TabGroup exactly (localStorage persistence,
// cross-component sync via synthetic storage events, optional controlled index).
-function createCrmTabState(props: CrmTabGroupProps) {
+// Takes the memoized `items` accessor (not props.items) so length checks read the
+// same single evaluation the render does — see the createMemo note in CrmTabGroup.
+function createCrmTabState(props: CrmTabGroupProps, items: () => CrmTabItem[]) {
const getInitialIndex = () => {
if (props.storageKey) {
const stored = localStorage.getItem(props.storageKey);
if (stored !== null) {
const parsed = parseInt(stored, 10);
- if (!isNaN(parsed) && parsed >= 0 && parsed < props.items.length) {
+ if (!isNaN(parsed) && parsed >= 0 && parsed < items().length) {
return parsed;
}
}
@@ -65,7 +67,7 @@ function createCrmTabState(props: CrmTabGroupProps) {
const handler = (e: StorageEvent) => {
if (e.key !== props.storageKey || e.newValue == null) return;
const n = parseInt(e.newValue, 10);
- if (!isNaN(n) && n >= 0 && n < props.items.length && n !== _activeIndex()) {
+ if (!isNaN(n) && n >= 0 && n < items().length && n !== _activeIndex()) {
_setActiveIndex(n);
props.onTabChange && props.onTabChange(n);
}
@@ -103,11 +105,19 @@ const CRM_TAB_ACTIVE = "border-x border-t-2 border-t-sky-700 text-primary";
const CRM_TAB_BADGE = "inline-flex items-center justify-center min-w-5 h-5 px-1 text-xs font-semibold bg-primary text-white rounded-full";
export function CrmTabGroup(props: CrmTabGroupProps) {
- const { activeIndex, setActiveIndex } = createCrmTabState(props);
+ // Memoize props.items: callers pass items={[{content: