@almadar/ui 5.124.0 → 5.125.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.
@@ -23315,13 +23315,13 @@ function DataGrid({
23315
23315
  }
23316
23316
  ),
23317
23317
  /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
23318
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center", children: [
23318
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
23319
23319
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
23320
23320
  /* @__PURE__ */ jsxRuntime.jsx(
23321
23321
  Typography,
23322
23322
  {
23323
23323
  variant: titleField?.variant === "h3" ? "h3" : "h4",
23324
- className: "font-semibold truncate",
23324
+ className: "font-semibold truncate min-w-0",
23325
23325
  children: String(titleValue)
23326
23326
  }
23327
23327
  )
@@ -23379,7 +23379,7 @@ function DataGrid({
23379
23379
  )
23380
23380
  ] }, field.name);
23381
23381
  }) }) }),
23382
- primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end", children: [
23382
+ primaryActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "justify-end flex-wrap", children: [
23383
23383
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
23384
23384
  Button,
23385
23385
  {
@@ -28392,7 +28392,7 @@ function TableView({
28392
28392
  col.key
28393
28393
  );
28394
28394
  }),
28395
- hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true })
28395
+ hasActions && /* @__PURE__ */ jsxRuntime.jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
28396
28396
  ]
28397
28397
  }
28398
28398
  );
@@ -28436,41 +28436,54 @@ function TableView({
28436
28436
  }
28437
28437
  return /* @__PURE__ */ jsxRuntime.jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
28438
28438
  }),
28439
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
28440
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
28441
- Button,
28442
- {
28443
- variant: action.variant ?? "ghost",
28444
- size: "sm",
28445
- onClick: handleActionClick(action, row),
28446
- "data-testid": `action-${action.event}`,
28447
- "data-row-id": String(row.id),
28448
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
28449
- children: [
28450
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
28451
- action.label
28452
- ]
28453
- },
28454
- i
28455
- )),
28456
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
28457
- Menu,
28458
- {
28459
- position: "bottom-end",
28460
- trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28461
- items: itemActions.slice(maxInlineActions).map((action) => ({
28462
- label: action.label,
28463
- icon: action.icon,
28464
- event: action.event,
28465
- variant: action.variant === "danger" ? "danger" : "default",
28466
- onClick: () => eventBus.emit(`UI:${action.event}`, {
28467
- id: row.id,
28468
- row
28469
- })
28470
- }))
28471
- }
28472
- )
28473
- ] })
28439
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
28440
+ HStack,
28441
+ {
28442
+ gap: "xs",
28443
+ className: cn(
28444
+ // Pinned: the fixed column tracks routinely overflow the caller's
28445
+ // scroll container, which used to leave the actions off-screen.
28446
+ // Opaque so scrolled cells pass underneath it.
28447
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
28448
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
28449
+ ),
28450
+ children: [
28451
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
28452
+ Button,
28453
+ {
28454
+ variant: action.variant ?? "ghost",
28455
+ size: "sm",
28456
+ onClick: handleActionClick(action, row),
28457
+ "data-testid": `action-${action.event}`,
28458
+ "data-row-id": String(row.id),
28459
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
28460
+ children: [
28461
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
28462
+ action.label
28463
+ ]
28464
+ },
28465
+ i
28466
+ )),
28467
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsxRuntime.jsx(
28468
+ Menu,
28469
+ {
28470
+ position: "bottom-end",
28471
+ trigger: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28472
+ items: itemActions.slice(maxInlineActions).map((action) => ({
28473
+ label: action.label,
28474
+ icon: action.icon,
28475
+ event: action.event,
28476
+ variant: action.variant === "danger" ? "danger" : "default",
28477
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
28478
+ id: row.id,
28479
+ row
28480
+ })
28481
+ }))
28482
+ }
28483
+ )
28484
+ ]
28485
+ }
28486
+ )
28474
28487
  ]
28475
28488
  }
28476
28489
  );
@@ -37205,6 +37218,16 @@ function normalizeFieldDefs(fields) {
37205
37218
  return String(f3);
37206
37219
  });
37207
37220
  }
37221
+ function buildFieldLabelMap(fields) {
37222
+ const map = {};
37223
+ if (!fields) return map;
37224
+ for (const f3 of fields) {
37225
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
37226
+ map[f3.key] = f3.header;
37227
+ }
37228
+ }
37229
+ return map;
37230
+ }
37208
37231
  function buildFieldTypeMap(fields) {
37209
37232
  const map = {};
37210
37233
  if (!fields) return map;
@@ -37256,6 +37279,8 @@ var init_DetailPanel = __esm({
37256
37279
  };
37257
37280
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
37258
37281
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
37282
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
37283
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
37259
37284
  const handleActionClick = React84.useCallback(
37260
37285
  (action, data2) => {
37261
37286
  if (action.navigatesTo) {
@@ -37291,7 +37316,7 @@ var init_DetailPanel = __esm({
37291
37316
  if (typeof field === "string") {
37292
37317
  const value = getNestedValue(normalizedData, field);
37293
37318
  return {
37294
- label: formatFieldLabel(field),
37319
+ label: labelFor(field),
37295
37320
  value: formatFieldValue(value, field),
37296
37321
  icon: getFieldIcon(field)
37297
37322
  };
@@ -37302,7 +37327,10 @@ var init_DetailPanel = __esm({
37302
37327
  }
37303
37328
  if (normalizedData && effectiveFieldNames) {
37304
37329
  const primaryField = effectiveFieldNames[0];
37305
- if (!title && primaryField && normalizedData[primaryField]) {
37330
+ const titleDerivedFromPrimary = Boolean(
37331
+ !title && primaryField && normalizedData[primaryField]
37332
+ );
37333
+ if (titleDerivedFromPrimary) {
37306
37334
  title = String(normalizedData[primaryField]);
37307
37335
  }
37308
37336
  const statusFields = effectiveFieldNames.filter(
@@ -37321,16 +37349,16 @@ var init_DetailPanel = __esm({
37321
37349
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
37322
37350
  );
37323
37351
  const otherFields = effectiveFieldNames.filter(
37324
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
37352
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
37325
37353
  );
37326
37354
  sections = [];
37327
37355
  if (statusFields.length > 0 || otherFields.length > 0) {
37328
37356
  const overviewFields = [];
37329
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
37357
+ [...statusFields, ...otherFields].forEach((field) => {
37330
37358
  const value = getNestedValue(normalizedData, field);
37331
37359
  if (value !== void 0 && value !== null) {
37332
37360
  overviewFields.push({
37333
- label: formatFieldLabel(field),
37361
+ label: labelFor(field),
37334
37362
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37335
37363
  icon: getFieldIcon(field)
37336
37364
  });
@@ -37346,7 +37374,7 @@ var init_DetailPanel = __esm({
37346
37374
  const value = getNestedValue(normalizedData, field);
37347
37375
  if (value !== void 0 && value !== null) {
37348
37376
  metricsFields.push({
37349
- label: formatFieldLabel(field),
37377
+ label: labelFor(field),
37350
37378
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37351
37379
  icon: getFieldIcon(field)
37352
37380
  });
@@ -37362,7 +37390,7 @@ var init_DetailPanel = __esm({
37362
37390
  const value = getNestedValue(normalizedData, field);
37363
37391
  if (value !== void 0 && value !== null) {
37364
37392
  timelineFields.push({
37365
- label: formatFieldLabel(field),
37393
+ label: labelFor(field),
37366
37394
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37367
37395
  icon: getFieldIcon(field)
37368
37396
  });
@@ -37378,7 +37406,7 @@ var init_DetailPanel = __esm({
37378
37406
  const value = getNestedValue(normalizedData, field);
37379
37407
  if (value !== void 0 && value !== null) {
37380
37408
  descFields.push({
37381
- label: formatFieldLabel(field),
37409
+ label: labelFor(field),
37382
37410
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37383
37411
  icon: getFieldIcon(field)
37384
37412
  });
@@ -37426,7 +37454,7 @@ var init_DetailPanel = __esm({
37426
37454
  if (typeof field === "string") {
37427
37455
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
37428
37456
  allFields.push({
37429
- label: formatFieldLabel(field),
37457
+ label: labelFor(field),
37430
37458
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37431
37459
  icon: getFieldIcon(field)
37432
37460
  });
@@ -45536,6 +45564,28 @@ function extractRouteParams2(pattern, path) {
45536
45564
  function pathMatches2(pattern, path) {
45537
45565
  return matchPath2(pattern, path) !== null;
45538
45566
  }
45567
+ function comparePathSpecificity2(a, b) {
45568
+ const aParts = a.split("/").filter(Boolean);
45569
+ const bParts = b.split("/").filter(Boolean);
45570
+ const shared = Math.min(aParts.length, bParts.length);
45571
+ for (let i = 0; i < shared; i++) {
45572
+ const aParam = aParts[i].startsWith(":");
45573
+ const bParam = bParts[i].startsWith(":");
45574
+ if (aParam !== bParam) return aParam ? 1 : -1;
45575
+ }
45576
+ return 0;
45577
+ }
45578
+ function matchPathAmong2(candidates, path, pathOf) {
45579
+ const routed = candidates.filter((candidate) => Boolean(pathOf(candidate)));
45580
+ const ranked = [...routed].sort(
45581
+ (a, b) => comparePathSpecificity2(pathOf(a), pathOf(b))
45582
+ );
45583
+ for (const candidate of ranked) {
45584
+ const params = matchPath2(pathOf(candidate), path);
45585
+ if (params !== null) return { candidate, params };
45586
+ }
45587
+ return null;
45588
+ }
45539
45589
  function isInlineOrbital(orbital) {
45540
45590
  return "name" in orbital && typeof orbital.name === "string";
45541
45591
  }
@@ -45544,21 +45594,22 @@ function isInlinePage(page) {
45544
45594
  }
45545
45595
  function findPageByPath2(schema, path) {
45546
45596
  if (!schema.orbitals) return null;
45597
+ const candidates = [];
45547
45598
  for (const orbital of schema.orbitals) {
45548
45599
  if (!isInlineOrbital(orbital)) continue;
45549
45600
  if (!orbital.pages) continue;
45550
45601
  for (const pageRef of orbital.pages) {
45551
45602
  if (!isInlinePage(pageRef)) continue;
45552
- const page = pageRef;
45553
- const pagePath = page.path;
45554
- if (!pagePath) continue;
45555
- const params = matchPath2(pagePath, path);
45556
- if (params !== null) {
45557
- return { page, params, orbitalName: orbital.name };
45558
- }
45603
+ candidates.push({ page: pageRef, orbitalName: orbital.name });
45559
45604
  }
45560
45605
  }
45561
- return null;
45606
+ const hit = matchPathAmong2(candidates, path, (entry) => entry.page.path);
45607
+ if (!hit) return null;
45608
+ return {
45609
+ page: hit.candidate.page,
45610
+ params: hit.params,
45611
+ orbitalName: hit.candidate.orbitalName
45612
+ };
45562
45613
  }
45563
45614
  function findPageByName2(schema, pageName) {
45564
45615
  if (!schema.orbitals) return null;
@@ -46181,12 +46232,14 @@ exports.TraitScopeProvider = TraitScopeProvider3;
46181
46232
  exports.UserContext = UserContext;
46182
46233
  exports.UserProvider = UserProvider;
46183
46234
  exports.VerificationProvider = VerificationProvider;
46235
+ exports.comparePathSpecificity = comparePathSpecificity2;
46184
46236
  exports.extractRouteParams = extractRouteParams2;
46185
46237
  exports.findPageByName = findPageByName2;
46186
46238
  exports.findPageByPath = findPageByPath2;
46187
46239
  exports.getAllPages = getAllPages2;
46188
46240
  exports.getDefaultPage = getDefaultPage2;
46189
46241
  exports.matchPath = matchPath2;
46242
+ exports.matchPathAmong = matchPathAmong2;
46190
46243
  exports.pathMatches = pathMatches2;
46191
46244
  exports.useActivePage = useActivePage2;
46192
46245
  exports.useCurrentPagePath = useCurrentPagePath2;
@@ -3,8 +3,8 @@ import { EntityRow, FieldValue } from '@almadar/core';
3
3
  import { U as UIThemeDefinition } from '../UserContext-BKckAUv7.cjs';
4
4
  export { A as ANONYMOUS_USER, a as CurrentPagePathContext, b as CurrentPagePathProvider, c as CurrentPagePathProviderProps, d as DesignThemeProvider, O as OrbitalThemeProvider, e as OrbitalThemeProviderProps, h as UserContext, i as UserContextValue, j as UserData, k as UserProvider, l as UserProviderProps, u as useCurrentPagePath, m as useDesignTheme, n as useHasPermission, o as useHasRole, q as useUser, r as useUserForEvaluation } from '../UserContext-BKckAUv7.cjs';
5
5
  export { E as EntitySchemaContextValue, a as EntitySchemaProvider, b as EntitySchemaProviderProps, S as SendEventResult, c as ServerBridgeContextValue, d as ServerBridgeProvider, e as ServerBridgeProviderProps, f as ServerBridgeTransport, g as ServerClientEffect, h as ServerResponseMeta, T as TraitContext, i as TraitContextValue, j as TraitInstance, k as TraitProvider, l as TraitProviderProps, u as useEntitySchema, m as useEntitySchemaOptional, n as useServerBridge, o as useTrait, p as useTraitContext } from '../TraitProvider-Ch79cUcb.cjs';
6
- import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-aRuNznZx.cjs';
7
- export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as extractRouteParams, f as findPageByName, g as findPageByPath, h as getAllPages, i as getDefaultPage, m as matchPath, p as pathMatches, u as useActivePage, j as useInitPayload, k as useNavigateTo, l as useNavigation, n as useNavigationId, o as useNavigationState } from '../offline-executor-aRuNznZx.cjs';
6
+ import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-Qz4b6GpF.cjs';
7
+ export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as comparePathSpecificity, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState } from '../offline-executor-Qz4b6GpF.cjs';
8
8
  import { E as EventBusContextType } from '../event-bus-types-Bl78kokd.cjs';
9
9
  export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-CPGwD49P.cjs';
10
10
  import '@almadar/core/patterns';
@@ -3,8 +3,8 @@ import { EntityRow, FieldValue } from '@almadar/core';
3
3
  import { U as UIThemeDefinition } from '../UserContext-BKckAUv7.js';
4
4
  export { A as ANONYMOUS_USER, a as CurrentPagePathContext, b as CurrentPagePathProvider, c as CurrentPagePathProviderProps, d as DesignThemeProvider, O as OrbitalThemeProvider, e as OrbitalThemeProviderProps, h as UserContext, i as UserContextValue, j as UserData, k as UserProvider, l as UserProviderProps, u as useCurrentPagePath, m as useDesignTheme, n as useHasPermission, o as useHasRole, q as useUser, r as useUserForEvaluation } from '../UserContext-BKckAUv7.js';
5
5
  export { E as EntitySchemaContextValue, a as EntitySchemaProvider, b as EntitySchemaProviderProps, S as SendEventResult, c as ServerBridgeContextValue, d as ServerBridgeProvider, e as ServerBridgeProviderProps, f as ServerBridgeTransport, g as ServerClientEffect, h as ServerResponseMeta, T as TraitContext, i as TraitContextValue, j as TraitInstance, k as TraitProvider, l as TraitProviderProps, u as useEntitySchema, m as useEntitySchemaOptional, n as useServerBridge, o as useTrait, p as useTraitContext } from '../TraitProvider-Ch79cUcb.js';
6
- import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-aRuNznZx.js';
7
- export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as extractRouteParams, f as findPageByName, g as findPageByPath, h as getAllPages, i as getDefaultPage, m as matchPath, p as pathMatches, u as useActivePage, j as useInitPayload, k as useNavigateTo, l as useNavigation, n as useNavigationId, o as useNavigationState } from '../offline-executor-aRuNznZx.js';
6
+ import { U as UseOfflineExecutorResult, a as UseOfflineExecutorOptions } from '../offline-executor-Qz4b6GpF.js';
7
+ export { N as NavigationContextValue, b as NavigationProvider, c as NavigationProviderProps, d as NavigationState, e as comparePathSpecificity, f as extractRouteParams, g as findPageByName, h as findPageByPath, i as getAllPages, j as getDefaultPage, m as matchPath, k as matchPathAmong, p as pathMatches, u as useActivePage, l as useInitPayload, n as useNavigateTo, o as useNavigation, q as useNavigationId, r as useNavigationState } from '../offline-executor-Qz4b6GpF.js';
8
8
  import { E as EventBusContextType } from '../event-bus-types-Bl78kokd.js';
9
9
  export { G as GameAudioContext, a as GameAudioContextValue, b as GameAudioProvider, c as GameAudioProviderProps, u as useGameAudioContext, d as useGameAudioContextOptional } from '../GameAudioProvider-CPGwD49P.js';
10
10
  import '@almadar/core/patterns';
@@ -23240,13 +23240,13 @@ function DataGrid({
23240
23240
  }
23241
23241
  ),
23242
23242
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
23243
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
23243
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center min-w-0", children: [
23244
23244
  titleField?.icon && renderIconInput(titleField.icon, { size: "sm", className: "text-primary flex-shrink-0" }),
23245
23245
  /* @__PURE__ */ jsx(
23246
23246
  Typography,
23247
23247
  {
23248
23248
  variant: titleField?.variant === "h3" ? "h3" : "h4",
23249
- className: "font-semibold truncate",
23249
+ className: "font-semibold truncate min-w-0",
23250
23250
  children: String(titleValue)
23251
23251
  }
23252
23252
  )
@@ -23304,7 +23304,7 @@ function DataGrid({
23304
23304
  )
23305
23305
  ] }, field.name);
23306
23306
  }) }) }),
23307
- primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end", children: [
23307
+ primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-end flex-wrap", children: [
23308
23308
  (maxInlineActions != null ? primaryActions.slice(0, maxInlineActions) : primaryActions).map((action, idx) => /* @__PURE__ */ jsxs(
23309
23309
  Button,
23310
23310
  {
@@ -28317,7 +28317,7 @@ function TableView({
28317
28317
  col.key
28318
28318
  );
28319
28319
  }),
28320
- hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true })
28320
+ hasActions && /* @__PURE__ */ jsx(Box, { "aria-hidden": true, className: "sticky right-0 bg-[var(--color-surface-subtle)]" })
28321
28321
  ]
28322
28322
  }
28323
28323
  );
@@ -28361,41 +28361,54 @@ function TableView({
28361
28361
  }
28362
28362
  return /* @__PURE__ */ jsx(Box, { role: "cell", className: cellBase, children: /* @__PURE__ */ jsx("span", { className: "truncate", children: formatCell(raw, col.format) }) }, col.key);
28363
28363
  }),
28364
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "justify-end flex-shrink-0 opacity-60 group-hover:opacity-100 transition-opacity", children: [
28365
- (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
28366
- Button,
28367
- {
28368
- variant: action.variant ?? "ghost",
28369
- size: "sm",
28370
- onClick: handleActionClick(action, row),
28371
- "data-testid": `action-${action.event}`,
28372
- "data-row-id": String(row.id),
28373
- className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
28374
- children: [
28375
- action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
28376
- action.label
28377
- ]
28378
- },
28379
- i
28380
- )),
28381
- maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
28382
- Menu,
28383
- {
28384
- position: "bottom-end",
28385
- trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28386
- items: itemActions.slice(maxInlineActions).map((action) => ({
28387
- label: action.label,
28388
- icon: action.icon,
28389
- event: action.event,
28390
- variant: action.variant === "danger" ? "danger" : "default",
28391
- onClick: () => eventBus.emit(`UI:${action.event}`, {
28392
- id: row.id,
28393
- row
28394
- })
28395
- }))
28396
- }
28397
- )
28398
- ] })
28364
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsxs(
28365
+ HStack,
28366
+ {
28367
+ gap: "xs",
28368
+ className: cn(
28369
+ // Pinned: the fixed column tracks routinely overflow the caller's
28370
+ // scroll container, which used to leave the actions off-screen.
28371
+ // Opaque so scrolled cells pass underneath it.
28372
+ "justify-end flex-shrink-0 sticky right-0 z-[1] transition-colors",
28373
+ lk.striped && index % 2 === 1 ? "bg-[var(--color-surface-subtle)]" : "bg-[var(--color-card)] group-hover:bg-[var(--color-surface-subtle)]"
28374
+ ),
28375
+ children: [
28376
+ (maxInlineActions != null ? itemActions.slice(0, maxInlineActions) : itemActions).map((action, i) => /* @__PURE__ */ jsxs(
28377
+ Button,
28378
+ {
28379
+ variant: action.variant ?? "ghost",
28380
+ size: "sm",
28381
+ onClick: handleActionClick(action, row),
28382
+ "data-testid": `action-${action.event}`,
28383
+ "data-row-id": String(row.id),
28384
+ className: cn(action.variant === "danger" && "text-error hover:bg-error/10"),
28385
+ children: [
28386
+ action.icon && renderIconInput3(action.icon, { size: "xs", className: "mr-1" }),
28387
+ action.label
28388
+ ]
28389
+ },
28390
+ i
28391
+ )),
28392
+ maxInlineActions != null && itemActions.length > maxInlineActions && /* @__PURE__ */ jsx(
28393
+ Menu,
28394
+ {
28395
+ position: "bottom-end",
28396
+ trigger: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", "aria-label": t("common.actions"), "data-testid": "action-overflow", children: /* @__PURE__ */ jsx(Icon, { name: "more-horizontal", size: "xs" }) }),
28397
+ items: itemActions.slice(maxInlineActions).map((action) => ({
28398
+ label: action.label,
28399
+ icon: action.icon,
28400
+ event: action.event,
28401
+ variant: action.variant === "danger" ? "danger" : "default",
28402
+ onClick: () => eventBus.emit(`UI:${action.event}`, {
28403
+ id: row.id,
28404
+ row
28405
+ })
28406
+ }))
28407
+ }
28408
+ )
28409
+ ]
28410
+ }
28411
+ )
28399
28412
  ]
28400
28413
  }
28401
28414
  );
@@ -37130,6 +37143,16 @@ function normalizeFieldDefs(fields) {
37130
37143
  return String(f3);
37131
37144
  });
37132
37145
  }
37146
+ function buildFieldLabelMap(fields) {
37147
+ const map = {};
37148
+ if (!fields) return map;
37149
+ for (const f3 of fields) {
37150
+ if (typeof f3 === "object" && "key" in f3 && f3.header !== void 0 && f3.header !== "") {
37151
+ map[f3.key] = f3.header;
37152
+ }
37153
+ }
37154
+ return map;
37155
+ }
37133
37156
  function buildFieldTypeMap(fields) {
37134
37157
  const map = {};
37135
37158
  if (!fields) return map;
@@ -37181,6 +37204,8 @@ var init_DetailPanel = __esm({
37181
37204
  };
37182
37205
  const effectiveFieldNames = isFieldDefArray(propFields) ? normalizeFieldDefs(propFields) : fieldNames;
37183
37206
  const fieldTypeMap = isFieldDefArray(propFields) ? buildFieldTypeMap(propFields) : {};
37207
+ const fieldLabelMap = isFieldDefArray(propFields) ? buildFieldLabelMap(propFields) : {};
37208
+ const labelFor = (field) => fieldLabelMap[field] ?? formatFieldLabel(field);
37184
37209
  const handleActionClick = useCallback(
37185
37210
  (action, data2) => {
37186
37211
  if (action.navigatesTo) {
@@ -37216,7 +37241,7 @@ var init_DetailPanel = __esm({
37216
37241
  if (typeof field === "string") {
37217
37242
  const value = getNestedValue(normalizedData, field);
37218
37243
  return {
37219
- label: formatFieldLabel(field),
37244
+ label: labelFor(field),
37220
37245
  value: formatFieldValue(value, field),
37221
37246
  icon: getFieldIcon(field)
37222
37247
  };
@@ -37227,7 +37252,10 @@ var init_DetailPanel = __esm({
37227
37252
  }
37228
37253
  if (normalizedData && effectiveFieldNames) {
37229
37254
  const primaryField = effectiveFieldNames[0];
37230
- if (!title && primaryField && normalizedData[primaryField]) {
37255
+ const titleDerivedFromPrimary = Boolean(
37256
+ !title && primaryField && normalizedData[primaryField]
37257
+ );
37258
+ if (titleDerivedFromPrimary) {
37231
37259
  title = String(normalizedData[primaryField]);
37232
37260
  }
37233
37261
  const statusFields = effectiveFieldNames.filter(
@@ -37246,16 +37274,16 @@ var init_DetailPanel = __esm({
37246
37274
  (f3) => f3.toLowerCase().includes("description") || f3.toLowerCase().includes("note")
37247
37275
  );
37248
37276
  const otherFields = effectiveFieldNames.filter(
37249
- (f3) => f3 !== primaryField && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
37277
+ (f3) => (!titleDerivedFromPrimary || f3 !== primaryField) && !statusFields.includes(f3) && !progressFields.includes(f3) && !metricFields.includes(f3) && !dateFields.includes(f3) && !descriptionFields.includes(f3)
37250
37278
  );
37251
37279
  sections = [];
37252
37280
  if (statusFields.length > 0 || otherFields.length > 0) {
37253
37281
  const overviewFields = [];
37254
- [...statusFields, ...otherFields.slice(0, 3)].forEach((field) => {
37282
+ [...statusFields, ...otherFields].forEach((field) => {
37255
37283
  const value = getNestedValue(normalizedData, field);
37256
37284
  if (value !== void 0 && value !== null) {
37257
37285
  overviewFields.push({
37258
- label: formatFieldLabel(field),
37286
+ label: labelFor(field),
37259
37287
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37260
37288
  icon: getFieldIcon(field)
37261
37289
  });
@@ -37271,7 +37299,7 @@ var init_DetailPanel = __esm({
37271
37299
  const value = getNestedValue(normalizedData, field);
37272
37300
  if (value !== void 0 && value !== null) {
37273
37301
  metricsFields.push({
37274
- label: formatFieldLabel(field),
37302
+ label: labelFor(field),
37275
37303
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37276
37304
  icon: getFieldIcon(field)
37277
37305
  });
@@ -37287,7 +37315,7 @@ var init_DetailPanel = __esm({
37287
37315
  const value = getNestedValue(normalizedData, field);
37288
37316
  if (value !== void 0 && value !== null) {
37289
37317
  timelineFields.push({
37290
- label: formatFieldLabel(field),
37318
+ label: labelFor(field),
37291
37319
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37292
37320
  icon: getFieldIcon(field)
37293
37321
  });
@@ -37303,7 +37331,7 @@ var init_DetailPanel = __esm({
37303
37331
  const value = getNestedValue(normalizedData, field);
37304
37332
  if (value !== void 0 && value !== null) {
37305
37333
  descFields.push({
37306
- label: formatFieldLabel(field),
37334
+ label: labelFor(field),
37307
37335
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37308
37336
  icon: getFieldIcon(field)
37309
37337
  });
@@ -37351,7 +37379,7 @@ var init_DetailPanel = __esm({
37351
37379
  if (typeof field === "string") {
37352
37380
  const value = normalizedData ? getNestedValue(normalizedData, field) : void 0;
37353
37381
  allFields.push({
37354
- label: formatFieldLabel(field),
37382
+ label: labelFor(field),
37355
37383
  value: renderRichFieldValue(value, field, fieldTypeMap[field]),
37356
37384
  icon: getFieldIcon(field)
37357
37385
  });
@@ -45461,6 +45489,28 @@ function extractRouteParams2(pattern, path) {
45461
45489
  function pathMatches2(pattern, path) {
45462
45490
  return matchPath2(pattern, path) !== null;
45463
45491
  }
45492
+ function comparePathSpecificity2(a, b) {
45493
+ const aParts = a.split("/").filter(Boolean);
45494
+ const bParts = b.split("/").filter(Boolean);
45495
+ const shared = Math.min(aParts.length, bParts.length);
45496
+ for (let i = 0; i < shared; i++) {
45497
+ const aParam = aParts[i].startsWith(":");
45498
+ const bParam = bParts[i].startsWith(":");
45499
+ if (aParam !== bParam) return aParam ? 1 : -1;
45500
+ }
45501
+ return 0;
45502
+ }
45503
+ function matchPathAmong2(candidates, path, pathOf) {
45504
+ const routed = candidates.filter((candidate) => Boolean(pathOf(candidate)));
45505
+ const ranked = [...routed].sort(
45506
+ (a, b) => comparePathSpecificity2(pathOf(a), pathOf(b))
45507
+ );
45508
+ for (const candidate of ranked) {
45509
+ const params = matchPath2(pathOf(candidate), path);
45510
+ if (params !== null) return { candidate, params };
45511
+ }
45512
+ return null;
45513
+ }
45464
45514
  function isInlineOrbital(orbital) {
45465
45515
  return "name" in orbital && typeof orbital.name === "string";
45466
45516
  }
@@ -45469,21 +45519,22 @@ function isInlinePage(page) {
45469
45519
  }
45470
45520
  function findPageByPath2(schema, path) {
45471
45521
  if (!schema.orbitals) return null;
45522
+ const candidates = [];
45472
45523
  for (const orbital of schema.orbitals) {
45473
45524
  if (!isInlineOrbital(orbital)) continue;
45474
45525
  if (!orbital.pages) continue;
45475
45526
  for (const pageRef of orbital.pages) {
45476
45527
  if (!isInlinePage(pageRef)) continue;
45477
- const page = pageRef;
45478
- const pagePath = page.path;
45479
- if (!pagePath) continue;
45480
- const params = matchPath2(pagePath, path);
45481
- if (params !== null) {
45482
- return { page, params, orbitalName: orbital.name };
45483
- }
45528
+ candidates.push({ page: pageRef, orbitalName: orbital.name });
45484
45529
  }
45485
45530
  }
45486
- return null;
45531
+ const hit = matchPathAmong2(candidates, path, (entry) => entry.page.path);
45532
+ if (!hit) return null;
45533
+ return {
45534
+ page: hit.candidate.page,
45535
+ params: hit.params,
45536
+ orbitalName: hit.candidate.orbitalName
45537
+ };
45487
45538
  }
45488
45539
  function findPageByName2(schema, pageName) {
45489
45540
  if (!schema.orbitals) return null;
@@ -46077,4 +46128,4 @@ function GameAudioProvider2({
46077
46128
  }
46078
46129
  GameAudioProvider2.displayName = "GameAudioProvider";
46079
46130
 
46080
- export { ANONYMOUS_USER, CurrentPagePathContext, CurrentPagePathProvider, EntitySchemaProvider, EventBusContext2 as EventBusContext, EventBusProvider, GameAudioContext2 as GameAudioContext, GameAudioProvider2 as GameAudioProvider, NavigationProvider2 as NavigationProvider, OfflineModeProvider, OrbitalProvider, OrbitalThemeProvider, SelectionContext, SelectionProvider, ServerBridgeProvider, TraitContext, TraitProvider, TraitScopeProvider3 as TraitScopeProvider, UserContext, UserProvider, VerificationProvider, extractRouteParams2 as extractRouteParams, findPageByName2 as findPageByName, findPageByPath2 as findPageByPath, getAllPages2 as getAllPages, getDefaultPage2 as getDefaultPage, matchPath2 as matchPath, pathMatches2 as pathMatches, useActivePage2 as useActivePage, useCurrentPagePath2 as useCurrentPagePath, useEntitySchema, useEntitySchemaOptional6 as useEntitySchemaOptional, useGameAudioContext2 as useGameAudioContext, useGameAudioContextOptional2 as useGameAudioContextOptional, useHasPermission, useHasRole, useInitPayload2 as useInitPayload, useNavigateTo2 as useNavigateTo, useNavigation2 as useNavigation, useNavigationId2 as useNavigationId, useNavigationState2 as useNavigationState, useOfflineMode, useOptionalOfflineMode, useSelection, useSelectionOptional, useServerBridge, useTrait, useTraitContext, useTraitScope, useTraitScopeChain2 as useTraitScopeChain, useUser, useUserForEvaluation };
46131
+ export { ANONYMOUS_USER, CurrentPagePathContext, CurrentPagePathProvider, EntitySchemaProvider, EventBusContext2 as EventBusContext, EventBusProvider, GameAudioContext2 as GameAudioContext, GameAudioProvider2 as GameAudioProvider, NavigationProvider2 as NavigationProvider, OfflineModeProvider, OrbitalProvider, OrbitalThemeProvider, SelectionContext, SelectionProvider, ServerBridgeProvider, TraitContext, TraitProvider, TraitScopeProvider3 as TraitScopeProvider, UserContext, UserProvider, VerificationProvider, comparePathSpecificity2 as comparePathSpecificity, extractRouteParams2 as extractRouteParams, findPageByName2 as findPageByName, findPageByPath2 as findPageByPath, getAllPages2 as getAllPages, getDefaultPage2 as getDefaultPage, matchPath2 as matchPath, matchPathAmong2 as matchPathAmong, pathMatches2 as pathMatches, useActivePage2 as useActivePage, useCurrentPagePath2 as useCurrentPagePath, useEntitySchema, useEntitySchemaOptional6 as useEntitySchemaOptional, useGameAudioContext2 as useGameAudioContext, useGameAudioContextOptional2 as useGameAudioContextOptional, useHasPermission, useHasRole, useInitPayload2 as useInitPayload, useNavigateTo2 as useNavigateTo, useNavigation2 as useNavigation, useNavigationId2 as useNavigationId, useNavigationState2 as useNavigationState, useOfflineMode, useOptionalOfflineMode, useSelection, useSelectionOptional, useServerBridge, useTrait, useTraitContext, useTraitScope, useTraitScopeChain2 as useTraitScopeChain, useUser, useUserForEvaluation };
@@ -769,6 +769,10 @@ Object.defineProperty(exports, "NavigationProvider", {
769
769
  enumerable: true,
770
770
  get: function () { return providers.NavigationProvider; }
771
771
  });
772
+ Object.defineProperty(exports, "comparePathSpecificity", {
773
+ enumerable: true,
774
+ get: function () { return providers.comparePathSpecificity; }
775
+ });
772
776
  Object.defineProperty(exports, "extractRouteParams", {
773
777
  enumerable: true,
774
778
  get: function () { return providers.extractRouteParams; }
@@ -793,6 +797,10 @@ Object.defineProperty(exports, "matchPath", {
793
797
  enumerable: true,
794
798
  get: function () { return providers.matchPath; }
795
799
  });
800
+ Object.defineProperty(exports, "matchPathAmong", {
801
+ enumerable: true,
802
+ get: function () { return providers.matchPathAmong; }
803
+ });
796
804
  Object.defineProperty(exports, "pathMatches", {
797
805
  enumerable: true,
798
806
  get: function () { return providers.pathMatches; }