@almadar/ui 4.45.0 → 4.47.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.
@@ -1,5 +1,5 @@
1
1
  import * as React135 from 'react';
2
- import React135__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useLayoutEffect, lazy, useId } from 'react';
2
+ import React135__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
3
3
  import { EventBusContext, useTraitScope, OrbitalProvider, TraitScopeProvider, VerificationProvider } from '@almadar/ui/providers';
4
4
  import { createLogger, isLogLevelEnabled } from '@almadar/logger';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -5584,8 +5584,8 @@ function Seigaiha({ size, color, strokeWidth }) {
5584
5584
  [s, s * 0.1]
5585
5585
  ];
5586
5586
  for (const [cx, cy] of centers) {
5587
- for (let ring = 1; ring <= 3; ring++) {
5588
- const cr = r * (ring / 3);
5587
+ for (let ring2 = 1; ring2 <= 3; ring2++) {
5588
+ const cr = r * (ring2 / 3);
5589
5589
  paths.push(
5590
5590
  `M ${f(cx - cr)},${f(cy)} A ${f(cr)} ${f(cr)} 0 0 1 ${f(cx + cr)},${f(cy)}`
5591
5591
  );
@@ -5682,8 +5682,8 @@ function Arch({ size, color, strokeWidth }) {
5682
5682
  const h = size * 1.5;
5683
5683
  const cx = w / 2;
5684
5684
  const paths = [];
5685
- for (let ring = 0; ring < 4; ring++) {
5686
- const scale = 1 - ring * 0.2;
5685
+ for (let ring2 = 0; ring2 < 4; ring2++) {
5686
+ const scale = 1 - ring2 * 0.2;
5687
5687
  const archW = w * 0.48 * scale;
5688
5688
  const archH = h * 0.7 * scale;
5689
5689
  const baseY = h * 0.85;
@@ -5698,7 +5698,7 @@ function Arch({ size, color, strokeWidth }) {
5698
5698
  paths.push(
5699
5699
  `M ${f(lx)},${f(baseY)} A ${f(radius)} ${f(radius)} 0 0 1 ${f(cx)},${f(tipY)} A ${f(radius)} ${f(radius)} 0 0 1 ${f(rx)},${f(baseY)}`
5700
5700
  );
5701
- if (ring === 0) {
5701
+ if (ring2 === 0) {
5702
5702
  paths.push(`M ${f(lx)},${f(baseY)} L ${f(rx)},${f(baseY)}`);
5703
5703
  }
5704
5704
  }
@@ -6386,12 +6386,15 @@ function ControlButton({
6386
6386
  sizeMap3[size] ?? sizeMap3.md,
6387
6387
  shapeMap[shape] ?? shapeMap.circle,
6388
6388
  variantMap[variant] ?? variantMap.secondary,
6389
- actualPressed && "scale-95 brightness-110 border-white",
6389
+ actualPressed && "scale-95 brightness-110 border-foreground",
6390
6390
  disabled && "opacity-50 cursor-not-allowed",
6391
6391
  className
6392
6392
  ),
6393
6393
  children: [
6394
- icon && /* @__PURE__ */ jsx("span", { className: "text-2xl", children: icon }),
6394
+ icon && /* @__PURE__ */ jsx("span", { className: "text-2xl", children: typeof icon === "string" ? (() => {
6395
+ const I = resolveIcon(icon);
6396
+ return I ? /* @__PURE__ */ jsx(I, { className: "w-6 h-6" }) : null;
6397
+ })() : icon }),
6395
6398
  label && !icon && /* @__PURE__ */ jsx("span", { children: label })
6396
6399
  ]
6397
6400
  }
@@ -6403,6 +6406,7 @@ var init_ControlButton = __esm({
6403
6406
  "use client";
6404
6407
  init_cn();
6405
6408
  init_useEventBus();
6409
+ init_Icon();
6406
6410
  sizeMap3 = {
6407
6411
  sm: "w-10 h-10 text-sm",
6408
6412
  md: "w-14 h-14 text-base",
@@ -6415,9 +6419,9 @@ var init_ControlButton = __esm({
6415
6419
  square: "rounded-md"
6416
6420
  };
6417
6421
  variantMap = {
6418
- primary: "bg-blue-600 text-[var(--color-foreground)] border-blue-400 hover:bg-blue-500",
6419
- secondary: "bg-[var(--color-surface,#374151)] text-[var(--color-foreground)] border-gray-500 hover:bg-gray-600",
6420
- ghost: "bg-transparent text-[var(--color-foreground)] border-white/30 hover:bg-white/10"
6422
+ primary: "bg-primary text-primary-foreground border-primary hover:bg-primary-hover",
6423
+ secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
6424
+ ghost: "bg-transparent text-foreground border-border hover:bg-muted"
6421
6425
  };
6422
6426
  ControlButton.displayName = "ControlButton";
6423
6427
  }
@@ -7145,8 +7149,8 @@ function ChoiceButton({
7145
7149
  className: cn(
7146
7150
  "w-full text-left px-4 py-2.5 rounded-md border transition-all duration-150",
7147
7151
  "flex items-center gap-2",
7148
- selected ? "bg-yellow-500/20 border-yellow-400 text-yellow-300" : "bg-white/5 border-white/10 text-[var(--color-foreground)] hover:bg-white/10 hover:border-white/30",
7149
- disabled && "opacity-40 cursor-not-allowed hover:bg-white/5 hover:border-white/10",
7152
+ selected ? "bg-accent/15 border-accent text-foreground" : "bg-muted/40 border-border text-foreground hover:bg-muted hover:border-border",
7153
+ disabled && "opacity-40 cursor-not-allowed hover:bg-muted/40 hover:border-border",
7150
7154
  className
7151
7155
  ),
7152
7156
  children: [
@@ -7155,7 +7159,7 @@ function ChoiceButton({
7155
7159
  {
7156
7160
  className: cn(
7157
7161
  "flex-shrink-0 font-mono font-bold text-sm",
7158
- selected ? "text-yellow-400" : "text-gray-500"
7162
+ selected ? "text-accent" : "text-muted-foreground"
7159
7163
  ),
7160
7164
  children: [
7161
7165
  index,
@@ -7196,7 +7200,7 @@ function ActionButton({
7196
7200
  disabled: isDisabled,
7197
7201
  onClick,
7198
7202
  className: cn(
7199
- "relative inline-flex items-center gap-1.5 rounded-md border font-medium text-[var(--color-foreground)] overflow-hidden transition-colors duration-150",
7203
+ "relative inline-flex items-center gap-1.5 rounded-md border font-medium overflow-hidden transition-colors duration-150",
7200
7204
  sizes.button,
7201
7205
  variantStyles8[variant],
7202
7206
  isDisabled && "opacity-60 cursor-not-allowed",
@@ -7206,7 +7210,7 @@ function ActionButton({
7206
7210
  onCooldown && /* @__PURE__ */ jsx(
7207
7211
  "div",
7208
7212
  {
7209
- className: "absolute inset-0 bg-black/60 pointer-events-none",
7213
+ className: "absolute inset-0 bg-foreground/40 pointer-events-none",
7210
7214
  style: {
7211
7215
  clipPath: `conic-gradient(from 0deg, transparent ${360 - cooldownDeg}deg, black ${360 - cooldownDeg}deg)`,
7212
7216
  WebkitClipPath: `conic-gradient(from 0deg, transparent ${360 - cooldownDeg}deg, black ${360 - cooldownDeg}deg)`,
@@ -7214,13 +7218,16 @@ function ActionButton({
7214
7218
  }
7215
7219
  }
7216
7220
  ),
7217
- icon && /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: icon }),
7221
+ icon && /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
7222
+ const I = resolveIcon(icon);
7223
+ return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
7224
+ })() : icon }),
7218
7225
  /* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
7219
7226
  hotkey && /* @__PURE__ */ jsx(
7220
7227
  "span",
7221
7228
  {
7222
7229
  className: cn(
7223
- "absolute top-0.5 right-0.5 bg-black/50 text-gray-300 rounded font-mono leading-tight",
7230
+ "absolute top-0.5 right-0.5 bg-foreground/30 text-primary-foreground rounded font-mono leading-tight",
7224
7231
  sizes.hotkey
7225
7232
  ),
7226
7233
  children: hotkey
@@ -7234,15 +7241,16 @@ var sizeMap13, variantStyles8;
7234
7241
  var init_ActionButton = __esm({
7235
7242
  "components/atoms/game/ActionButton.tsx"() {
7236
7243
  init_cn();
7244
+ init_Icon();
7237
7245
  sizeMap13 = {
7238
7246
  sm: { button: "px-3 py-1.5 text-xs", hotkey: "text-[9px] px-1", icon: "text-xs" },
7239
7247
  md: { button: "px-4 py-2 text-sm", hotkey: "text-[10px] px-1.5", icon: "text-sm" },
7240
7248
  lg: { button: "px-5 py-2.5 text-base", hotkey: "text-xs px-2", icon: "text-base" }
7241
7249
  };
7242
7250
  variantStyles8 = {
7243
- primary: "bg-blue-600 hover:bg-blue-500 border-blue-400/40",
7244
- secondary: "bg-gray-700 hover:bg-gray-600 border-gray-500/40",
7245
- danger: "bg-red-700 hover:bg-red-600 border-red-400/40"
7251
+ primary: "bg-primary text-primary-foreground hover:bg-primary-hover border-primary",
7252
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
7253
+ danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
7246
7254
  };
7247
7255
  ActionButton.displayName = "ActionButton";
7248
7256
  }
@@ -17893,20 +17901,20 @@ function CanvasEffectEngine({
17893
17901
  return img?.complete ? img : void 0;
17894
17902
  }, []);
17895
17903
  useEffect(() => {
17896
- const now = performance.now();
17904
+ const now2 = performance.now();
17897
17905
  const effectX = x || width / 2;
17898
17906
  const effectY = y || height / 2;
17899
17907
  const preset = presets[actionType](effectX, effectY);
17900
17908
  const state = stateRef.current;
17901
17909
  for (const emitter of preset.particles) {
17902
17910
  const scaledEmitter = { ...emitter, count: Math.round(emitter.count * intensity) };
17903
- state.particles.push(...spawnParticles(scaledEmitter, now));
17911
+ state.particles.push(...spawnParticles(scaledEmitter, now2));
17904
17912
  }
17905
17913
  for (const seqConfig of preset.sequences) {
17906
- state.sequences.push(spawnSequence(seqConfig, now));
17914
+ state.sequences.push(spawnSequence(seqConfig, now2));
17907
17915
  }
17908
17916
  for (const ovConfig of preset.overlays) {
17909
- state.overlays.push(spawnOverlay(ovConfig, now));
17917
+ state.overlays.push(spawnOverlay(ovConfig, now2));
17910
17918
  }
17911
17919
  if (preset.screenShake > 0) {
17912
17920
  shakeRef.current.intensity = preset.screenShake * intensity;
@@ -21288,7 +21296,7 @@ function DataGrid({
21288
21296
  onClick: handleActionClick(action, itemData),
21289
21297
  "data-testid": `action-${action.event}`,
21290
21298
  "data-row-id": String(itemData.id),
21291
- className: "text-error hover:bg-error/10 px-2",
21299
+ className: "text-error hover:text-error hover:bg-error/10 px-2",
21292
21300
  children: [
21293
21301
  action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs" }),
21294
21302
  action.label
@@ -23817,7 +23825,7 @@ function StatBadge({
23817
23825
  const I = resolveIcon(icon);
23818
23826
  return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : icon;
23819
23827
  })() : icon }),
23820
- /* @__PURE__ */ jsx("span", { className: "text-gray-400 font-medium", children: label }),
23828
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-medium", children: label }),
23821
23829
  format === "hearts" && max && /* @__PURE__ */ jsx(
23822
23830
  HealthBar,
23823
23831
  {
@@ -23844,7 +23852,7 @@ function StatBadge({
23844
23852
  animated: true
23845
23853
  }
23846
23854
  ),
23847
- format === "text" && /* @__PURE__ */ jsx("span", { className: "font-bold text-[var(--color-foreground)]", children: value })
23855
+ format === "text" && /* @__PURE__ */ jsx("span", { className: "font-bold text-foreground", children: value })
23848
23856
  ]
23849
23857
  }
23850
23858
  );
@@ -23862,11 +23870,11 @@ var init_StatBadge = __esm({
23862
23870
  lg: "text-base px-4 py-2"
23863
23871
  };
23864
23872
  variantMap2 = {
23865
- default: "bg-[var(--color-card)]/80 border-gray-700",
23866
- primary: "bg-blue-900/80 border-blue-700",
23867
- success: "bg-green-900/80 border-green-700",
23868
- warning: "bg-yellow-900/80 border-yellow-700",
23869
- danger: "bg-red-900/80 border-red-700"
23873
+ default: "bg-card/80 border-border text-foreground",
23874
+ primary: "bg-primary/15 border-primary/40 text-foreground",
23875
+ success: "bg-success/15 border-success/40 text-foreground",
23876
+ warning: "bg-warning/15 border-warning/40 text-foreground",
23877
+ danger: "bg-error/15 border-error/40 text-foreground"
23870
23878
  };
23871
23879
  StatBadge.displayName = "StatBadge";
23872
23880
  }
@@ -36301,9 +36309,45 @@ var init_List = __esm({
36301
36309
  List3.displayName = "List";
36302
36310
  }
36303
36311
  });
36304
- var DefaultEmptyDetail, MasterDetail;
36312
+ function MasterDetail({
36313
+ entity,
36314
+ masterFields,
36315
+ detailFields: _detailFields,
36316
+ // Captured but not used here - detail handled separately
36317
+ loading: externalLoading,
36318
+ isLoading: externalIsLoading,
36319
+ error: externalError,
36320
+ className,
36321
+ ...rest
36322
+ }) {
36323
+ const loading = externalLoading ?? false;
36324
+ const isLoading = externalIsLoading ?? false;
36325
+ const error = externalError ?? null;
36326
+ return /* @__PURE__ */ jsx(
36327
+ DataTable,
36328
+ {
36329
+ fields: masterFields,
36330
+ columns: masterFields,
36331
+ entity,
36332
+ isLoading: loading || isLoading,
36333
+ error,
36334
+ className,
36335
+ emptyTitle: "No items found",
36336
+ emptyDescription: "Create your first item to get started.",
36337
+ ...rest
36338
+ }
36339
+ );
36340
+ }
36305
36341
  var init_MasterDetail = __esm({
36306
- "components/organisms/layout/MasterDetail.tsx"() {
36342
+ "components/organisms/MasterDetail.tsx"() {
36343
+ "use client";
36344
+ init_DataTable();
36345
+ MasterDetail.displayName = "MasterDetail";
36346
+ }
36347
+ });
36348
+ var DefaultEmptyDetail, MasterDetailLayout;
36349
+ var init_MasterDetailLayout = __esm({
36350
+ "components/organisms/layout/MasterDetailLayout.tsx"() {
36307
36351
  init_cn();
36308
36352
  init_Typography();
36309
36353
  DefaultEmptyDetail = () => /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full border-2 border-dashed border-border", children: /* @__PURE__ */ jsx(
@@ -36314,7 +36358,7 @@ var init_MasterDetail = __esm({
36314
36358
  children: "Select an item to view details"
36315
36359
  }
36316
36360
  ) });
36317
- MasterDetail = ({
36361
+ MasterDetailLayout = ({
36318
36362
  master,
36319
36363
  detail,
36320
36364
  emptyDetail,
@@ -36349,7 +36393,7 @@ var init_MasterDetail = __esm({
36349
36393
  }
36350
36394
  );
36351
36395
  };
36352
- MasterDetail.displayName = "MasterDetail";
36396
+ MasterDetailLayout.displayName = "MasterDetailLayout";
36353
36397
  }
36354
36398
  });
36355
36399
  var COLUMN_CLASSES, ASPECT_CLASSES, MediaGallery;
@@ -38767,7 +38811,7 @@ function getAllEvents(traits2) {
38767
38811
  }
38768
38812
  function EventDispatcherTab({ traits: traits2, schema }) {
38769
38813
  const eventBus = useEventBus();
38770
- const [log11, setLog] = React135.useState([]);
38814
+ const [log12, setLog] = React135.useState([]);
38771
38815
  const prevStatesRef = React135.useRef(/* @__PURE__ */ new Map());
38772
38816
  React135.useEffect(() => {
38773
38817
  for (const trait of traits2) {
@@ -38831,9 +38875,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
38831
38875
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: "Other Events (not available from current state)" }),
38832
38876
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
38833
38877
  ] }),
38834
- log11.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
38878
+ log12.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
38835
38879
  /* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: "Recent Transitions" }),
38836
- /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
38880
+ /* @__PURE__ */ jsx(Stack, { gap: "xs", children: log12.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
38837
38881
  /* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
38838
38882
  " ",
38839
38883
  /* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -43104,6 +43148,7 @@ var init_component_registry_generated = __esm({
43104
43148
  init_LoadingState();
43105
43149
  init_MarkdownContent();
43106
43150
  init_MasterDetail();
43151
+ init_MasterDetailLayout();
43107
43152
  init_MatrixQuestion();
43108
43153
  init_MediaGallery();
43109
43154
  init_Meter();
@@ -43398,6 +43443,7 @@ var init_component_registry_generated = __esm({
43398
43443
  "MapViewPattern": MapViewPattern,
43399
43444
  "MarkdownContent": MarkdownContent,
43400
43445
  "MasterDetail": MasterDetail,
43446
+ "MasterDetailLayout": MasterDetailLayout,
43401
43447
  "MatrixQuestion": MatrixQuestion,
43402
43448
  "MediaGallery": MediaGallery,
43403
43449
  "Menu": MenuPattern,
@@ -45628,6 +45674,103 @@ function ServerBridgeProvider({
45628
45674
  // runtime/OrbPreview.tsx
45629
45675
  init_navigation();
45630
45676
  init_verificationRegistry();
45677
+ var PERF_NAMESPACE = "almadar:perf:canvas";
45678
+ var log11 = createLogger(PERF_NAMESPACE);
45679
+ var RING_SIZE = 50;
45680
+ var ring = [];
45681
+ var writeIdx = 0;
45682
+ var subscribers = /* @__PURE__ */ new Set();
45683
+ function push(entry) {
45684
+ if (ring.length < RING_SIZE) {
45685
+ ring.push(entry);
45686
+ } else {
45687
+ ring[writeIdx] = entry;
45688
+ }
45689
+ writeIdx = (writeIdx + 1) % RING_SIZE;
45690
+ for (const fn of subscribers) fn();
45691
+ }
45692
+ function isEnabled() {
45693
+ return isLogLevelEnabled("DEBUG", PERF_NAMESPACE);
45694
+ }
45695
+ function now() {
45696
+ return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
45697
+ }
45698
+ function perfStart(name) {
45699
+ if (!isEnabled()) return -1;
45700
+ if (typeof performance !== "undefined" && typeof performance.mark === "function") {
45701
+ try {
45702
+ performance.mark(`${name}-start`);
45703
+ } catch {
45704
+ }
45705
+ }
45706
+ return now();
45707
+ }
45708
+ function perfEnd(name, startToken, detail) {
45709
+ if (startToken < 0 || !isEnabled()) return;
45710
+ const endTs = now();
45711
+ const durationMs = endTs - startToken;
45712
+ if (typeof performance !== "undefined" && typeof performance.measure === "function") {
45713
+ try {
45714
+ performance.mark(`${name}-end`);
45715
+ performance.measure(name, `${name}-start`, `${name}-end`);
45716
+ } catch {
45717
+ }
45718
+ }
45719
+ push({ name, durationMs, ts: endTs, detail });
45720
+ log11.debug(name, () => ({ durationMs, ...detail ?? {} }));
45721
+ }
45722
+ function perfTime(name, fn, detail) {
45723
+ const t = perfStart(name);
45724
+ try {
45725
+ return fn();
45726
+ } finally {
45727
+ perfEnd(name, t, detail);
45728
+ }
45729
+ }
45730
+ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
45731
+ if (!isEnabled()) return;
45732
+ push({
45733
+ name: `profiler:${id}:${phase}`,
45734
+ durationMs: actualDuration,
45735
+ ts: commitTime,
45736
+ detail: { baseDuration }
45737
+ });
45738
+ log11.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
45739
+ };
45740
+ function getPerfSnapshot() {
45741
+ if (ring.length < RING_SIZE) return ring.slice();
45742
+ return [...ring.slice(writeIdx), ...ring.slice(0, writeIdx)];
45743
+ }
45744
+ var cachedSnapshot = [];
45745
+ var cachedRevision = -1;
45746
+ var revision = 0;
45747
+ function bumpRevision() {
45748
+ revision++;
45749
+ }
45750
+ subscribers.add(bumpRevision);
45751
+ function getSnapshot2() {
45752
+ if (cachedRevision !== revision) {
45753
+ cachedSnapshot = getPerfSnapshot();
45754
+ cachedRevision = revision;
45755
+ }
45756
+ return cachedSnapshot;
45757
+ }
45758
+ function subscribe(fn) {
45759
+ subscribers.add(fn);
45760
+ return () => {
45761
+ subscribers.delete(fn);
45762
+ };
45763
+ }
45764
+ function usePerfBuffer() {
45765
+ return useSyncExternalStore(subscribe, getSnapshot2, getSnapshot2);
45766
+ }
45767
+ function clearPerf() {
45768
+ ring.length = 0;
45769
+ writeIdx = 0;
45770
+ for (const fn of subscribers) fn();
45771
+ }
45772
+
45773
+ // runtime/prepareSchemaForPreview.ts
45631
45774
  function generateEntityRow(entity, idx) {
45632
45775
  const row = { id: String(idx) };
45633
45776
  for (const f3 of entity.fields) {
@@ -45653,6 +45796,7 @@ function generateFieldValue(entityName, field, idx) {
45653
45796
  }
45654
45797
  }
45655
45798
  function buildMockData(schema) {
45799
+ const t = perfStart("build-mock-data");
45656
45800
  const result = {};
45657
45801
  for (const orbital of schema.orbitals) {
45658
45802
  const entity = orbital.entity;
@@ -45670,6 +45814,7 @@ function buildMockData(schema) {
45670
45814
  );
45671
45815
  result[entityName] = rows;
45672
45816
  }
45817
+ perfEnd("build-mock-data", t, { orbitalCount: schema.orbitals.length, entityCount: Object.keys(result).length });
45673
45818
  return result;
45674
45819
  }
45675
45820
  function isInlineTrait2(traitRef) {
@@ -46186,4 +46331,4 @@ function BrowserPlayground({
46186
46331
  );
46187
46332
  }
46188
46333
 
46189
- export { BrowserPlayground, EntitySchemaProvider, OrbPreview, ServerBridgeProvider, TraitContext, TraitProvider, adjustSchemaForMockData, buildMockData, clearSchemaCache, createClientEffectHandlers, prepareSchemaForPreview, useEntityDefinition, useEntitySchema, useEntitySchemaOptional, useResolvedSchema, useServerBridge, useTrait, useTraitContext, useTraitStateMachine, wrapCallbackForEvent };
46334
+ export { BrowserPlayground, EntitySchemaProvider, OrbPreview, PERF_NAMESPACE, ServerBridgeProvider, TraitContext, TraitProvider, adjustSchemaForMockData, buildMockData, clearPerf, clearSchemaCache, createClientEffectHandlers, getPerfSnapshot, perfEnd, perfStart, perfTime, prepareSchemaForPreview, profilerOnRender, useEntityDefinition, useEntitySchema, useEntitySchemaOptional, usePerfBuffer, useResolvedSchema, useServerBridge, useTrait, useTraitContext, useTraitStateMachine, wrapCallbackForEvent };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @almadar/ui/runtime — perf instrumentation
3
+ *
4
+ * Ring buffer of timing events for the canvas render path, plus mark/measure
5
+ * helpers and a React.Profiler onRender callback. All gated behind
6
+ * `createLogger('almadar:perf:canvas')` so production (LOG_LEVEL >= WARN)
7
+ * skips both `performance.*` calls and the ring write.
8
+ */
9
+ import type { ProfilerOnRenderCallback } from 'react';
10
+ export declare const PERF_NAMESPACE = "almadar:perf:canvas";
11
+ export interface PerfEntry {
12
+ readonly name: string;
13
+ readonly durationMs: number;
14
+ readonly ts: number;
15
+ readonly detail?: Readonly<Record<string, unknown>>;
16
+ }
17
+ /**
18
+ * Start a phase. Returns an opaque token; pass to {@link perfEnd}.
19
+ * Returns -1 when the namespace is gated off, so call sites pay only one comparison.
20
+ */
21
+ export declare function perfStart(name: string): number;
22
+ export declare function perfEnd(name: string, startToken: number, detail?: Record<string, unknown>): void;
23
+ /** Synchronous wrapper that times a fn end-to-end. */
24
+ export declare function perfTime<T>(name: string, fn: () => T, detail?: Record<string, unknown>): T;
25
+ /**
26
+ * React.Profiler `onRender` callback. Records `actualDuration` per commit.
27
+ * Phase is `"mount" | "update" | "nested-update"`.
28
+ */
29
+ export declare const profilerOnRender: ProfilerOnRenderCallback;
30
+ /** Snapshot in insertion order (oldest first). Stable identity until next push. */
31
+ export declare function getPerfSnapshot(): readonly PerfEntry[];
32
+ /** React hook: returns the current ring snapshot, re-renders on push. */
33
+ export declare function usePerfBuffer(): readonly PerfEntry[];
34
+ export declare function clearPerf(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "4.45.0",
3
+ "version": "4.47.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [