@rebasepro/app 0.13.0 → 0.13.1-canary.g18cfeb7

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.
Files changed (43) hide show
  1. package/dist/collections/entity-display-cache.d.ts +75 -0
  2. package/dist/collections/entity-display.d.ts +35 -0
  3. package/dist/collections/entity_image_preview.d.ts +8 -0
  4. package/dist/collections/form-layout.d.ts +6 -0
  5. package/dist/collections/index.d.ts +4 -0
  6. package/dist/collections/property-path.d.ts +16 -0
  7. package/dist/collections/property_presentation.d.ts +18 -3
  8. package/dist/collections/summary-property.d.ts +65 -0
  9. package/dist/components/common/useColumnsIds.d.ts +22 -0
  10. package/dist/core/Rebase.d.ts +1 -1
  11. package/dist/core/RebaseProps.d.ts +10 -2
  12. package/dist/index.es.js +660 -210
  13. package/dist/index.es.js.map +1 -1
  14. package/dist/util/entity_cache.d.ts +19 -3
  15. package/dist/util/previews.d.ts +19 -0
  16. package/package.json +7 -7
  17. package/src/auth/useRebaseAuthController.ts +9 -1
  18. package/src/collections/entity-display-cache.ts +195 -0
  19. package/src/collections/entity-display.ts +105 -0
  20. package/src/collections/entity_image_preview.ts +13 -0
  21. package/src/collections/form-layout.ts +8 -1
  22. package/src/collections/index.ts +4 -0
  23. package/src/collections/property-path.ts +30 -0
  24. package/src/collections/property_presentation.ts +39 -4
  25. package/src/collections/summary-property.ts +114 -0
  26. package/src/collections/title-property.ts +13 -16
  27. package/src/components/common/useColumnsIds.tsx +67 -29
  28. package/src/core/Rebase.tsx +20 -2
  29. package/src/core/RebaseProps.tsx +10 -2
  30. package/src/hooks/data/useCollection.tsx +20 -3
  31. package/src/hooks/data/useFetch.tsx +17 -2
  32. package/src/hooks/data/useRelationSelector.tsx +20 -4
  33. package/src/hooks/useAuthSubscription.ts +18 -2
  34. package/src/hooks/useBuildLocalConfigurationPersistence.tsx +20 -12
  35. package/src/locales/de.ts +1 -0
  36. package/src/locales/en.ts +2 -0
  37. package/src/locales/es.ts +1 -0
  38. package/src/locales/fr.ts +1 -0
  39. package/src/locales/hi.ts +2 -1
  40. package/src/locales/it.ts +1 -0
  41. package/src/locales/pt.ts +1 -0
  42. package/src/util/entity_cache.ts +34 -31
  43. package/src/util/previews.ts +49 -29
package/dist/index.es.js CHANGED
@@ -1,19 +1,19 @@
1
1
  import React, { createContext, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
2
2
  import { Alert, AlertCircleIcon, AlertTriangleIcon, AppWindow, ArrowDownIcon, ArrowLeftIcon, ArrowRightLeftIcon, ArrowUpDownIcon, ArrowUpIcon, Avatar, BooleanSwitch, Button, CalendarIcon, Card, CenteredView, CheckCircle2Icon, CheckIcon, Checkbox, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, Chip, CircleDotIcon, CircleUserIcon, CircularProgress, ColumnsIcon, Container, Dialog, DialogActions, DialogContent, DialogTitle, DollarSignIcon, ErrorBoundary, FileIcon, FileTextIcon, FilterChip, FilterIcon, FolderIcon, FolderKanbanIcon, IconButton, KanbanIcon, KanbanView, LanguagesIcon, LayoutGridIcon, ListIcon, ListPlusIcon, ListTodoIcon, LoadingButton, LogOutIcon, MailIcon, Menu, MenuItem, MessageCircleIcon, MoonIcon, MultiSelect, MultiSelectItem, PanelLeftIcon, Paper, PenLineIcon, PencilIcon, PhoneIcon, PinIcon, PlusIcon, Popover, RefreshCwIcon, SearchBar, Select, SelectItem, Separator, SettingsIcon, Skeleton, StickyNoteIcon, SunIcon, SunMoonIcon, Tab, Table, TableBody, TableCell, TableHeader, TableRow, Tabs, TagIcon, TextField, ToggleButtonGroup, Tooltip, Trash2Icon, TrendingUpIcon, TypeIcon, Typography, UserIcon, UserPlus, UsersIcon, VideoIcon, Wand2Icon, WrenchIcon, XIcon, cls, colorClassesMapping, coolIconKeys, defaultBorderMixin, getColorSchemeForKey, getColorSchemeForSeed, iconKeys, iconSize, lucideIcons } from "@rebasepro/ui";
3
3
  import { ALL_WHERE_FILTER_OPS, DEFAULT_DATA_SOURCE_KEY, DEFAULT_FILTERABLE_RELATION_KINDS, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, RebaseApiError, Vector, getDataSourceCapabilities, isLazyComponentRef, isRelationalCollectionConfig } from "@rebasepro/types";
4
+ import { UNRENDERED_SLOTS, resolveAdminCollection } from "@rebasepro/admin-types";
4
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
6
  import { SnackbarProvider as SnackbarProvider$1, useSnackbar } from "notistack";
6
- import { buildRebaseData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, evaluateCondition, getEntityChildViews, getLabelOrConfigFrom, getPrimaryKeys, getSubcollections, isPropertyBuilder, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, stripCollectionPath, wrapAsEntityData, wrapAsSdkData } from "@rebasepro/common";
7
+ import { buildRebaseData, canCreateEntity, canDeleteEntity, canEditEntity, canReadCollection, evaluateCondition, getChildViewDeclaringProperties, getChildViewRelationPropertyKeys, getEntityChildViews, getLabelOrConfigFrom, getPrimaryKeys, getSubcollections, isPropertyBuilder, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource, stripCollectionPath, wrapAsEntityData, wrapAsSdkData } from "@rebasepro/common";
7
8
  import { getTitlePropertyKey as getTitlePropertyKey$1, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1 } from "@rebasepro/app";
8
9
  import { Link, Routes, createBrowserRouter, useBlocker, useLocation } from "react-router";
9
- import { generateForeignKeyName, hashString, isObject, isPlainObject, mergeDeep, randomString, slugify } from "@rebasepro/utils";
10
+ import { generateForeignKeyName, hashString, isArrayValue, isObject, isPlainObject, isRecordValue, mergeDeep, randomString, readStoredJson, slugify, writeStoredJson } from "@rebasepro/utils";
10
11
  import { I18nextProvider, initReactI18next, useTranslation as useTranslation$1 } from "react-i18next";
11
12
  import { deepEqual } from "fast-equals";
12
13
  import i18next from "i18next";
13
14
  import { RouterProvider } from "react-router/dom";
14
15
  import Fuse from "fuse.js";
15
16
  import Compressor from "compressorjs";
16
- import { resolveAdminCollection } from "@rebasepro/admin-types";
17
17
  //#region src/core/PluginProviderStack.tsx
18
18
  /**
19
19
  * Wraps children with all provider components from plugins that match the given scope.
@@ -414,6 +414,97 @@ function SchemaDriftBanner({ className }) {
414
414
  });
415
415
  }
416
416
  //#endregion
417
+ //#region src/collections/summary-property.ts
418
+ /**
419
+ * Whether a property can stand in for a record in *one line*.
420
+ *
421
+ * Preview surfaces — the reference card, a list row, a board card — have a
422
+ * single line per property and no way to grow. That is a constraint on the
423
+ * *value*, not on the property's importance: a Markdown biography is the most
424
+ * interesting column on an author, and it is still the wrong thing to paste
425
+ * into a 44px card. Ranking it here means the picker can skip it without
426
+ * anyone having to configure their way out of a broken card.
427
+ *
428
+ * Kept beside {@link getTitlePropertyCandidates} because the two answer
429
+ * neighbouring questions — "what is this record called" and "what else can be
430
+ * said about it in a line" — from the same property schema.
431
+ *
432
+ * @group Collections
433
+ */
434
+ var SUMMARY_RANK = {
435
+ /**
436
+ * No single-line form exists. A map renders as a key/value table and an
437
+ * array of maps as a stack of them; there is no first line to take.
438
+ */
439
+ UNUSABLE: 0,
440
+ /**
441
+ * Readable in one line, but only as an excerpt — the property holds a
442
+ * document, and the line is its opening. Worth showing when nothing better
443
+ * is available, never in preference to a value that fits whole.
444
+ */
445
+ EXCERPT: 1,
446
+ /** Short and self-contained: the line *is* the value. */
447
+ DIRECT: 2
448
+ };
449
+ /**
450
+ * File-storage backed content (single image, array of images, generic upload…).
451
+ * Preview surfaces give these their own image slot, so they are never also a
452
+ * text line.
453
+ *
454
+ * @group Collections
455
+ */
456
+ function isStorageProperty(property) {
457
+ if (!property) return false;
458
+ if (property.type === "string" && (property.storage || property.admin?.urlPreview === "image")) return true;
459
+ if (property.type === "array" && property.of && !Array.isArray(property.of)) {
460
+ const inner = property.of;
461
+ if (inner.type === "string" && (inner.storage || inner.admin?.urlPreview === "image")) return true;
462
+ }
463
+ return false;
464
+ }
465
+ /**
466
+ * True when the property holds free text long enough to be a document rather
467
+ * than a value — Markdown, or an explicitly multi-line string.
468
+ *
469
+ * @group Collections
470
+ */
471
+ function isLongTextProperty(property) {
472
+ if (!property || property.type !== "string") return false;
473
+ return Boolean(property.admin?.markdown || property.admin?.multiline);
474
+ }
475
+ /**
476
+ * How well a property reads as one line of a preview. See {@link SUMMARY_RANK}.
477
+ *
478
+ * Decided from the property *schema* only: no values are consulted, so the
479
+ * answer is stable for a collection and can be memoised per collection rather
480
+ * than per row.
481
+ *
482
+ * @group Collections
483
+ */
484
+ function rankSummaryProperty(property) {
485
+ if (!property) return SUMMARY_RANK.UNUSABLE;
486
+ if (isStorageProperty(property)) return SUMMARY_RANK.UNUSABLE;
487
+ if (property.type === "map") return SUMMARY_RANK.UNUSABLE;
488
+ if (property.type === "array") {
489
+ if (Array.isArray(property.of)) return SUMMARY_RANK.UNUSABLE;
490
+ if (!property.of) return property.oneOf ? SUMMARY_RANK.UNUSABLE : SUMMARY_RANK.DIRECT;
491
+ const innerRank = rankSummaryProperty(property.of);
492
+ if (innerRank === SUMMARY_RANK.UNUSABLE) return SUMMARY_RANK.UNUSABLE;
493
+ if (innerRank === SUMMARY_RANK.EXCERPT) return SUMMARY_RANK.UNUSABLE;
494
+ return SUMMARY_RANK.DIRECT;
495
+ }
496
+ if (isLongTextProperty(property)) return SUMMARY_RANK.EXCERPT;
497
+ return SUMMARY_RANK.DIRECT;
498
+ }
499
+ /**
500
+ * True when the property has any single-line form at all.
501
+ *
502
+ * @group Collections
503
+ */
504
+ function canSummariseProperty(property) {
505
+ return rankSummaryProperty(property) !== SUMMARY_RANK.UNUSABLE;
506
+ }
507
+ //#endregion
417
508
  //#region src/util/previews.ts
418
509
  function isReferenceProperty(property) {
419
510
  if (!property) return null;
@@ -434,35 +525,47 @@ function isHiddenProperty(property) {
434
525
  return Boolean(property.admin?.hideFromCollection);
435
526
  }
436
527
  /**
437
- * Returns true when the property holds file-storage content (single image,
438
- * array of images, generic upload, …). These properties are rendered by the
439
- * dedicated image-slot and should NOT appear as regular preview columns.
528
+ * The properties a preview surface should render for a record, best first.
529
+ *
530
+ * Three things decide the answer, in this order:
531
+ *
532
+ * 1. `previewProperties` — passed in, or declared on the collection. A stated
533
+ * list is returned verbatim, ranking and limit included: a developer who
534
+ * asks for the Markdown biography gets the Markdown biography.
535
+ * 2. Whether the value has a one-line form at all. A map renders as a
536
+ * key/value table and a Markdown field as a document; neither fits a card
537
+ * line, so they never take a slot from a value that does. See
538
+ * {@link rankSummaryProperty}.
539
+ * 3. `propertiesOrder`, which breaks ties.
540
+ *
541
+ * The middle step is the one that is easy to get wrong by leaving out.
542
+ * `propertiesOrder` states the *column* order of a collection table — it is
543
+ * not a statement that the first three columns summarise a record, and reading
544
+ * it as one is how a card ends up rendering somebody's entire biography.
440
545
  */
441
- function isStorageProperty$1(property) {
442
- if (!property) return false;
443
- if (property.type === "string" && property.storage) return true;
444
- if (property.type === "string" && property.admin?.urlPreview === "image") return true;
445
- if (property.type === "array" && property.of && !Array.isArray(property.of)) {
446
- const inner = property.of;
447
- if (inner.type === "string" && (inner.storage || inner.admin?.urlPreview === "image")) return true;
448
- }
449
- return false;
450
- }
451
546
  function getEntityPreviewKeys(authController, targetCollection, fields, previewProperties, limit = 3) {
452
547
  const allProperties = Object.keys(targetCollection.properties);
453
548
  let listProperties = previewProperties?.filter((p) => allProperties.includes(p));
454
549
  if (!listProperties && targetCollection.previewProperties) listProperties = targetCollection.previewProperties?.filter((p) => allProperties.includes(p));
455
550
  if (listProperties && listProperties.length > 0) return listProperties;
456
- else {
457
- listProperties = targetCollection.propertiesOrder || allProperties;
458
- return listProperties.filter((key) => {
459
- const prop = targetCollection.properties[key];
460
- return !(prop && typeof prop === "object" && "isId" in prop && Boolean(prop.isId)) && key !== "id";
461
- }).filter((key) => {
462
- const property = targetCollection.properties[key];
463
- return property && !isPropertyBuilder(property) && !isReferenceProperty(property) && !isRelationProperty(property) && !isHiddenProperty(property) && !isStorageProperty$1(property);
464
- }).slice(0, limit);
551
+ const hasExplicitOrder = !!targetCollection.propertiesOrder;
552
+ const order = targetCollection.propertiesOrder || allProperties;
553
+ const direct = [];
554
+ const excerpt = [];
555
+ for (const key of order) {
556
+ const property = targetCollection.properties[key];
557
+ if (!property || isPropertyBuilder(property)) continue;
558
+ if (key === "id" || "isId" in property && Boolean(property.isId)) continue;
559
+ if (isReferenceProperty(property)) continue;
560
+ if (!hasExplicitOrder && isRelationProperty(property)) continue;
561
+ if (isHiddenProperty(property)) continue;
562
+ if (isStorageProperty(property)) continue;
563
+ const rank = rankSummaryProperty(property);
564
+ if (rank === SUMMARY_RANK.UNUSABLE) continue;
565
+ (rank === SUMMARY_RANK.EXCERPT ? excerpt : direct).push(key);
566
+ if (direct.length >= limit) break;
465
567
  }
568
+ return [...direct, ...excerpt].slice(0, limit);
466
569
  }
467
570
  var INCLUDE_ALL_RELATIONS = ["*"];
468
571
  /**
@@ -544,6 +647,7 @@ function useCollection({ path, collection, filterValues, sortBy, itemCount, offs
544
647
  meta: res.meta
545
648
  }), onError);
546
649
  else {
650
+ let cancelled = false;
547
651
  accessor.find({
548
652
  where: whereParams,
549
653
  limit: itemCount,
@@ -552,11 +656,19 @@ function useCollection({ path, collection, filterValues, sortBy, itemCount, offs
552
656
  orderBy: orderByParams,
553
657
  searchString,
554
658
  include: includeParams
555
- }).then((res) => onEntitiesUpdate({
556
- data: res.data,
557
- meta: res.meta
558
- })).catch(onError);
559
- return () => {};
659
+ }).then((res) => {
660
+ if (cancelled) return;
661
+ onEntitiesUpdate({
662
+ data: res.data,
663
+ meta: res.meta
664
+ });
665
+ }).catch((e) => {
666
+ if (cancelled) return;
667
+ onError(e);
668
+ });
669
+ return () => {
670
+ cancelled = true;
671
+ };
560
672
  }
561
673
  }, [
562
674
  path,
@@ -836,8 +948,17 @@ function useFetch({ path, entityId, collection, databaseId, useCache = false })
836
948
  const accessor = dataClient.collection(path);
837
949
  if (accessor.listenById) return accessor.listenById(entityId, (entity) => onEntityUpdate(entity), onError);
838
950
  else {
839
- accessor.findById(entityId).then((entity) => onEntityUpdate(entity)).catch(onError);
840
- return () => {};
951
+ let cancelled = false;
952
+ accessor.findById(entityId).then((entity) => {
953
+ if (cancelled) return;
954
+ onEntityUpdate(entity);
955
+ }).catch((e) => {
956
+ if (cancelled) return;
957
+ onError(e);
958
+ });
959
+ return () => {
960
+ cancelled = true;
961
+ };
841
962
  }
842
963
  } else {
843
964
  onEntityUpdate(void 0);
@@ -948,6 +1069,7 @@ function useRelationSelector({ path, collection, fixedFilter, pageSize = DEFAULT
948
1069
  meta: res.meta
949
1070
  }), onErrorUpdate);
950
1071
  else {
1072
+ let cancelled = false;
951
1073
  accessor.find({
952
1074
  where: whereParams,
953
1075
  limit,
@@ -955,11 +1077,19 @@ function useRelationSelector({ path, collection, fixedFilter, pageSize = DEFAULT
955
1077
  orderBy: void 0,
956
1078
  searchString: currentSearch,
957
1079
  include: includeParams
958
- }).then((res) => onEntitiesUpdate({
959
- data: res.data,
960
- meta: res.meta
961
- })).catch(onErrorUpdate);
962
- unsubscribe = () => {};
1080
+ }).then((res) => {
1081
+ if (cancelled) return;
1082
+ onEntitiesUpdate({
1083
+ data: res.data,
1084
+ meta: res.meta
1085
+ });
1086
+ }).catch((e) => {
1087
+ if (cancelled) return;
1088
+ onErrorUpdate(e);
1089
+ });
1090
+ unsubscribe = () => {
1091
+ cancelled = true;
1092
+ };
963
1093
  }
964
1094
  unsubscribeRef.current = unsubscribe || null;
965
1095
  }, [
@@ -1254,7 +1384,15 @@ function useAuthSubscription(authClient) {
1254
1384
  setInitialLoading(true);
1255
1385
  authClient.getUser().then((user) => {
1256
1386
  if (user) setUser(user);
1257
- }).catch((e) => {}).finally(() => {
1387
+ setAuthError(void 0);
1388
+ }).catch((e) => {
1389
+ const status = e?.status;
1390
+ if (status === 401 || status === 403) {
1391
+ setAuthError(void 0);
1392
+ return;
1393
+ }
1394
+ setAuthError(e instanceof Error ? e : new Error(String(e)));
1395
+ }).finally(() => {
1258
1396
  setInitialLoading(false);
1259
1397
  });
1260
1398
  }
@@ -1630,8 +1768,10 @@ function useSlot(slot, props) {
1630
1768
  function useBuildLocalConfigurationPersistence() {
1631
1769
  const configCache = useRef({});
1632
1770
  const getCollectionFromStorage = useCallback((storageKey) => {
1633
- const item = localStorage.getItem(storageKey);
1634
- return item ? JSON.parse(item) : {};
1771
+ return readStoredJson(storageKey, {
1772
+ fallback: {},
1773
+ accept: isRecordValue
1774
+ });
1635
1775
  }, []);
1636
1776
  const getCollectionConfig = useCallback((path) => {
1637
1777
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
@@ -1640,29 +1780,33 @@ function useBuildLocalConfigurationPersistence() {
1640
1780
  }, [getCollectionFromStorage]);
1641
1781
  const onCollectionModified = useCallback((path, data) => {
1642
1782
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
1643
- localStorage.setItem(storageKey, JSON.stringify(data));
1783
+ writeStoredJson(storageKey, data);
1644
1784
  const cachedConfig = configCache.current[storageKey];
1645
- const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(path), data);
1785
+ const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(storageKey), data);
1646
1786
  configCache.current[storageKey] = mergeDeep(configCache.current[storageKey], newConfig);
1647
1787
  }, [getCollectionFromStorage]);
1648
1788
  const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState([]);
1649
1789
  const [favouritePaths, _setFavouritePaths] = useState([]);
1650
1790
  const [collapsedGroups, _setCollapsedGroups] = useState([]);
1651
1791
  useEffect(() => {
1652
- _setRecentlyVisitedPaths(localStorage.getItem("recently_visited_paths") ? JSON.parse(localStorage.getItem("recently_visited_paths")) : []);
1653
- _setFavouritePaths(localStorage.getItem("favourite_paths") ? JSON.parse(localStorage.getItem("favourite_paths")) : []);
1654
- _setCollapsedGroups(localStorage.getItem("collapsed_groups") ? JSON.parse(localStorage.getItem("collapsed_groups")) : []);
1792
+ const readPaths = (key) => readStoredJson(key, {
1793
+ fallback: [],
1794
+ accept: isArrayValue
1795
+ });
1796
+ _setRecentlyVisitedPaths(readPaths("recently_visited_paths"));
1797
+ _setFavouritePaths(readPaths("favourite_paths"));
1798
+ _setCollapsedGroups(readPaths("collapsed_groups"));
1655
1799
  }, []);
1656
1800
  const setRecentlyVisitedPaths = useCallback((paths) => {
1657
- localStorage.setItem("recently_visited_paths", JSON.stringify(paths));
1801
+ writeStoredJson("recently_visited_paths", paths);
1658
1802
  _setRecentlyVisitedPaths(paths);
1659
1803
  }, []);
1660
1804
  const setFavouritePaths = useCallback((paths) => {
1661
- localStorage.setItem("favourite_paths", JSON.stringify(paths));
1805
+ writeStoredJson("favourite_paths", paths);
1662
1806
  _setFavouritePaths(paths);
1663
1807
  }, []);
1664
1808
  const setCollapsedGroups = useCallback((paths) => {
1665
- localStorage.setItem("collapsed_groups", JSON.stringify(paths));
1809
+ writeStoredJson("collapsed_groups", paths);
1666
1810
  _setCollapsedGroups(paths);
1667
1811
  }, []);
1668
1812
  return useMemo(() => ({
@@ -2325,24 +2469,202 @@ function useDebouncedData(data, deps, timeoutMs = 5e3) {
2325
2469
  return immediateUpdate ? data : deferredData;
2326
2470
  }
2327
2471
  //#endregion
2328
- //#region src/components/common/useColumnsIds.tsx
2472
+ //#region src/collections/property-path.ts
2329
2473
  /**
2330
- * Get a property in a property tree from a dot-path like `address.street`.
2331
- * Inlined here to avoid importing property-aware utilities from the admin layer.
2474
+ * The property at a dotted path, walking `map` children — `address.street`.
2475
+ *
2476
+ * The value counterpart is `getValueInPath` in `@rebasepro/utils`; this is the
2477
+ * schema half, and the two have to be used together. Reading a dotted path off
2478
+ * an entity while looking its property up with a flat `properties[path]` gives
2479
+ * the value and `undefined` for how to render it, which is how a declared title
2480
+ * on a nested field silently fell back to a derived one.
2481
+ *
2482
+ * There were three copies of this: one private to `useColumnsIds`, one exported
2483
+ * from the admin layer, and the flat lookup in the title resolver that was not
2484
+ * this function at all. This is the one, in the lowest layer that needs it —
2485
+ * admin re-exports it under the name it already published.
2332
2486
  */
2333
2487
  function getPropertyInPath(properties, path) {
2334
- if (typeof properties === "object") {
2335
- if (path in properties) return properties[path];
2336
- if (path.includes(".")) {
2337
- const pathSegments = path.split(".");
2338
- const childProperty = properties[pathSegments[0]];
2339
- if (typeof childProperty === "object" && childProperty.type === "map" && childProperty.properties) return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join("."));
2488
+ if (typeof properties !== "object" || !properties) return void 0;
2489
+ if (path in properties) return properties[path];
2490
+ if (path.includes(".")) {
2491
+ const pathSegments = path.split(".");
2492
+ const childProperty = properties[pathSegments[0]];
2493
+ if (typeof childProperty === "object" && childProperty?.type === "map" && childProperty.properties) return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join("."));
2494
+ }
2495
+ }
2496
+ //#endregion
2497
+ //#region src/collections/property_presentation.ts
2498
+ /**
2499
+ * A condition stated as a literal rather than as a rule.
2500
+ *
2501
+ * `PropertyConditions` accepts either, and the two are answered in different
2502
+ * places: a rule needs a context and so can only be evaluated while rendering a
2503
+ * particular entity, but a literal is already the answer. Reading it here is
2504
+ * what makes `hidden: true` work without every gate below having to build a
2505
+ * condition context first — and without the caller reaching for
2506
+ * `{ "==": [1, 1] }` to say something it can say with a boolean.
2507
+ */
2508
+ function literalCondition(condition) {
2509
+ return condition === true;
2510
+ }
2511
+ function isReadOnly(property) {
2512
+ if (property.admin?.readOnly) return true;
2513
+ if (literalCondition(property.conditions?.readOnly)) return true;
2514
+ if (property.type === "date") {
2515
+ if (property.autoValue) return true;
2516
+ }
2517
+ if (property.type === "reference") return !property.path && !("Field" in (property.admin || {}) && property.admin?.Field);
2518
+ return false;
2519
+ }
2520
+ function isHidden(property) {
2521
+ if (literalCondition(property.conditions?.hidden)) return true;
2522
+ return typeof property.admin?.disabled === "object" && Boolean(property.admin?.disabled.hidden);
2523
+ }
2524
+ /**
2525
+ * Whether the field is disabled by its own declaration, ignoring form state.
2526
+ *
2527
+ * The `admin.disabled` block and `conditions.disabled: true` say the same thing
2528
+ * two ways, so every caller that gated on the first now asks here instead of
2529
+ * growing a second check of its own.
2530
+ */
2531
+ function isDisabled(property) {
2532
+ return Boolean(property.admin?.disabled) || literalCondition(property.conditions?.disabled);
2533
+ }
2534
+ function applyPropertyConditions(property, context) {
2535
+ const { conditions } = property;
2536
+ if (!conditions) return property;
2537
+ const result = { ...property };
2538
+ if (conditions.disabled) {
2539
+ if (evaluateCondition(conditions.disabled, context)) {
2540
+ result.admin = result.admin || {};
2541
+ result.admin.disabled = {
2542
+ clearOnDisabled: conditions.clearOnDisabled ?? false,
2543
+ disabledMessage: conditions.disabledMessage,
2544
+ hidden: false
2545
+ };
2546
+ }
2547
+ }
2548
+ if (conditions.hidden) {
2549
+ if (evaluateCondition(conditions.hidden, context)) {
2550
+ result.admin = result.admin || {};
2551
+ result.admin.disabled = {
2552
+ ...typeof result.admin?.disabled === "object" ? result.admin.disabled : {},
2553
+ hidden: true,
2554
+ clearOnDisabled: conditions.clearOnDisabled ?? false
2555
+ };
2340
2556
  }
2341
2557
  }
2558
+ if (conditions.readOnly) {
2559
+ if (evaluateCondition(conditions.readOnly, context)) {
2560
+ result.admin = result.admin || {};
2561
+ result.admin.readOnly = true;
2562
+ }
2563
+ }
2564
+ if (conditions.required !== void 0) {
2565
+ const isRequired = evaluateCondition(conditions.required, context);
2566
+ result.validation = {
2567
+ ...result.validation,
2568
+ required: isRequired,
2569
+ requiredMessage: conditions.requiredMessage
2570
+ };
2571
+ }
2572
+ if (context.isNew && conditions.defaultValue !== void 0) result.defaultValue = evaluateCondition(conditions.defaultValue, context);
2573
+ if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
2574
+ if (result.type === "reference") {
2575
+ if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
2576
+ if (conditions.referenceFilter) {
2577
+ result.admin = result.admin || {};
2578
+ result.admin.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
2579
+ }
2580
+ }
2581
+ if (result.type === "array") {
2582
+ if (conditions.canAddElements !== void 0) {
2583
+ result.admin = result.admin || {};
2584
+ result.admin.canAddElements = evaluateCondition(conditions.canAddElements, context);
2585
+ }
2586
+ if (conditions.sortable !== void 0) {
2587
+ result.admin = result.admin || {};
2588
+ result.admin.sortable = evaluateCondition(conditions.sortable, context);
2589
+ }
2590
+ }
2591
+ return result;
2342
2592
  }
2593
+ /**
2594
+ * Convert an object with numeric keys back to an array.
2595
+ * Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
2596
+ */
2597
+ function objectToArray(obj) {
2598
+ if (Array.isArray(obj)) return obj.map(String);
2599
+ if (obj && typeof obj === "object") {
2600
+ const keys = Object.keys(obj);
2601
+ if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
2602
+ }
2603
+ return [];
2604
+ }
2605
+ /**
2606
+ * Apply enum-specific conditions to filter and modify enum values.
2607
+ */
2608
+ function applyEnumConditions(enumValues, conditions, context) {
2609
+ let result = [...enumValues];
2610
+ if (conditions.allowedEnumValues) {
2611
+ const allowedArray = objectToArray(evaluateCondition(conditions.allowedEnumValues, context));
2612
+ if (allowedArray.length > 0) result = result.filter((ev) => allowedArray.includes(String(ev.id)));
2613
+ }
2614
+ if (conditions.excludedEnumValues) {
2615
+ const excludedArray = objectToArray(evaluateCondition(conditions.excludedEnumValues, context));
2616
+ if (excludedArray.length > 0) result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
2617
+ }
2618
+ if (conditions.enumConditions) result = result.map((ev) => {
2619
+ const evConditions = conditions.enumConditions?.[ev.id];
2620
+ if (!evConditions) return ev;
2621
+ if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) return null;
2622
+ if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) return {
2623
+ ...ev,
2624
+ disabled: true
2625
+ };
2626
+ return ev;
2627
+ }).filter((ev) => ev !== null);
2628
+ return result;
2629
+ }
2630
+ //#endregion
2631
+ //#region src/components/common/useColumnsIds.tsx
2343
2632
  function getSubcollectionColumnId(collection) {
2344
2633
  return `subcollection:${collection.slug}`;
2345
2634
  }
2635
+ /**
2636
+ * The jump-to-tab columns a collection table does not need, because the relation
2637
+ * behind them already has a column of its own.
2638
+ *
2639
+ * Every child view gets a 200px button column that opens its tab. For a relation
2640
+ * declared in `relations` that button is the relation's only presence in the
2641
+ * table. For one declared as a property it is the second: the property's own
2642
+ * column is already there, hydrated by the list fetch's `include: ["*"]`, showing
2643
+ * the child rows themselves — and carrying the *same heading*, because the tab
2644
+ * takes its name from the declaring property. Two columns called "Applications",
2645
+ * one of them a button, and nothing in the header to tell them apart.
2646
+ *
2647
+ * The property column wins: it shows what the children are, and each chip in it
2648
+ * opens one. The tab stays reachable by opening the record, which is what the
2649
+ * rest of the table's rows do anyway.
2650
+ *
2651
+ * Unless the author hid that column — `hideFromCollection` on the property, or a
2652
+ * `propertiesOrder` that omits it, is a statement about the column and not about
2653
+ * the relation, so the button comes back rather than the relation dropping out
2654
+ * of the table altogether.
2655
+ */
2656
+ function getRedundantChildViewColumnIds(collection) {
2657
+ const redundant = /* @__PURE__ */ new Set();
2658
+ const properties = collection.properties ?? {};
2659
+ const order = collection.propertiesOrder;
2660
+ for (const [viewKey, propertyKey] of getChildViewDeclaringProperties(collection)) {
2661
+ const property = properties[propertyKey];
2662
+ if (!property || property.admin?.hideFromCollection) continue;
2663
+ if (order && !order.includes(propertyKey)) continue;
2664
+ redundant.add(`subcollection:${viewKey}`);
2665
+ }
2666
+ return redundant;
2667
+ }
2346
2668
  function useColumnIds(collection, includeSubcollections) {
2347
2669
  return useMemo(() => {
2348
2670
  if (collection.propertiesOrder) return hideAndExpandKeys(collection, collection.propertiesOrder);
@@ -2357,8 +2679,10 @@ function hideAndExpandKeys(collection, keys) {
2357
2679
  if (rootProperty && rootProperty.type === "map" && rootProperty.admin?.spreadChildren && rootProperty.properties) rootsWithExplicitChildren.add(rootKey);
2358
2680
  }
2359
2681
  const processedPropertyKeys = /* @__PURE__ */ new Set();
2682
+ const redundantChildViewColumns = getRedundantChildViewColumnIds(collection);
2360
2683
  const result = keys.flatMap((key) => {
2361
2684
  if (processedPropertyKeys.has(key)) return [null];
2685
+ if (redundantChildViewColumns.has(key)) return [null];
2362
2686
  const property = collection.properties[key];
2363
2687
  if (property) {
2364
2688
  processedPropertyKeys.add(key);
@@ -2372,7 +2696,7 @@ function hideAndExpandKeys(collection, keys) {
2372
2696
  }
2373
2697
  return [{
2374
2698
  key,
2375
- disabled: Boolean(property.admin?.disabled) || Boolean(property.admin?.readOnly)
2699
+ disabled: isDisabled(property) || Boolean(property.admin?.readOnly)
2376
2700
  }];
2377
2701
  }
2378
2702
  if (key.includes(".")) {
@@ -2387,7 +2711,7 @@ function hideAndExpandKeys(collection, keys) {
2387
2711
  if (nestedProperty.admin?.disabled && typeof nestedProperty.admin?.disabled === "object" && nestedProperty.admin?.disabled.hidden) return [null];
2388
2712
  return [{
2389
2713
  key,
2390
- disabled: Boolean(rootProperty.admin?.disabled) || Boolean(rootProperty.admin?.readOnly) || Boolean(nestedProperty.admin?.disabled) || Boolean(nestedProperty.admin?.readOnly)
2714
+ disabled: isDisabled(rootProperty) || Boolean(rootProperty.admin?.readOnly) || isDisabled(nestedProperty) || Boolean(nestedProperty.admin?.readOnly)
2391
2715
  }];
2392
2716
  }
2393
2717
  }
@@ -2420,7 +2744,7 @@ function hideAndExpandKeys(collection, keys) {
2420
2744
  } else {
2421
2745
  result.push({
2422
2746
  key: propKey,
2423
- disabled: Boolean(property.admin?.disabled) || Boolean(property.admin?.readOnly)
2747
+ disabled: isDisabled(property) || Boolean(property.admin?.readOnly)
2424
2748
  });
2425
2749
  processedPropertyKeys.add(propKey);
2426
2750
  }
@@ -2439,14 +2763,20 @@ function getDefaultColumnKeys(collection, includeSubCollections) {
2439
2763
  return hideAndExpandKeys(collection, columnIds);
2440
2764
  }
2441
2765
  function getColumnKeysForProperty(property, key, disabled) {
2442
- if (property.type === "map" && property.admin?.spreadChildren && property.properties) return Object.entries(property.properties).flatMap(([childKey, childProperty]) => getColumnKeysForProperty(childProperty, `${key}.${childKey}`, disabled || Boolean(property.admin?.disabled) || Boolean(property.admin?.readOnly)));
2766
+ if (!property) return [];
2767
+ if (property.type === "map" && property.admin?.spreadChildren && property.properties) return Object.entries(property.properties).flatMap(([childKey, childProperty]) => getColumnKeysForProperty(childProperty, `${key}.${childKey}`, disabled || isDisabled(property) || Boolean(property.admin?.readOnly)));
2443
2768
  return [{
2444
2769
  key,
2445
- disabled: disabled || Boolean(property.admin?.disabled) || Boolean(property.admin?.readOnly)
2770
+ disabled: disabled || isDisabled(property) || Boolean(property.admin?.readOnly)
2446
2771
  }];
2447
2772
  }
2448
2773
  function getFormFieldKeys(collection) {
2449
- const propertyKeys = collection.properties ? Object.keys(collection.properties) : [];
2774
+ const properties = collection.properties ?? {};
2775
+ const consumedByChildView = getChildViewRelationPropertyKeys(collection);
2776
+ const propertyKeys = Object.keys(properties).filter((key) => {
2777
+ if (!consumedByChildView.has(key)) return true;
2778
+ return properties[key].admin?.renderInForm === true;
2779
+ });
2450
2780
  const additionalFields = collection.additionalFields ?? [];
2451
2781
  const allKeys = [...propertyKeys, ...additionalFields.map((field) => field.key)];
2452
2782
  if (collection.propertiesOrder) return collection.propertiesOrder.filter((key) => allKeys.includes(key));
@@ -9821,6 +10151,7 @@ var en = {
9821
10151
  column_cannot_be_edited: "This column can't be edited directly",
9822
10152
  close: "Close",
9823
10153
  hide_list: "Hide list",
10154
+ show_list: "Show list",
9824
10155
  unsaved_local_changes: "Unsaved Local changes",
9825
10156
  unsaved_local_changes_description: "This document was edited locally and has unsaved changes. These local changes will be lost if you don't apply them.",
9826
10157
  preview_changes: "Preview Changes",
@@ -10632,6 +10963,7 @@ var en = {
10632
10963
  inspect_record: "Inspect record",
10633
10964
  inspect: "Inspect",
10634
10965
  close_inspector: "Close inspector",
10966
+ open_in_live_site: "Open in the live site",
10635
10967
  history_revert: "Revert to this version",
10636
10968
  history_revert_title: "Revert data to this version?",
10637
10969
  history_reverted: "Reverted to selected version",
@@ -10720,6 +11052,7 @@ var es = {
10720
11052
  column_cannot_be_edited: "Esta columna no se puede editar directamente",
10721
11053
  close: "Cerrar",
10722
11054
  hide_list: "Ocultar lista",
11055
+ show_list: "Mostrar lista",
10723
11056
  unsaved_local_changes: "Cambios locales sin guardar",
10724
11057
  unsaved_local_changes_description: "Este documento ha sido editado localmente y tiene cambios sin guardar. Estos cambios locales se perderán si no los aplicas.",
10725
11058
  preview_changes: "Previsualizar cambios",
@@ -11576,6 +11909,7 @@ var de = {
11576
11909
  column_cannot_be_edited: "Diese Spalte kann nicht direkt bearbeitet werden",
11577
11910
  close: "Schließen",
11578
11911
  hide_list: "Liste ausblenden",
11912
+ show_list: "Liste einblenden",
11579
11913
  unsaved_local_changes: "Ungespeicherte lokale Änderungen",
11580
11914
  unsaved_local_changes_description: "Dieses Dokument wurde lokal bearbeitet und weist ungespeicherte Änderungen auf. Diese lokalen Änderungen gehen verloren, wenn Sie sie nicht anwenden.",
11581
11915
  preview_changes: "Änderungen in der Vorschau anzeigen",
@@ -12422,6 +12756,7 @@ var fr = {
12422
12756
  column_cannot_be_edited: "Cette colonne ne peut pas être modifiée directement",
12423
12757
  close: "Fermer",
12424
12758
  hide_list: "Masquer la liste",
12759
+ show_list: "Afficher la liste",
12425
12760
  unsaved_local_changes: "Modifications locales non enregistrées",
12426
12761
  unsaved_local_changes_description: "Ce document a été modifié localement et contient des modifications non enregistrées. Ces modifications locales seront perdues si vous ne les appliquez pas.",
12427
12762
  preview_changes: "Aperçu des modifications",
@@ -13268,6 +13603,7 @@ var it = {
13268
13603
  column_cannot_be_edited: "Questa colonna non può essere modificata direttamente",
13269
13604
  close: "Chiudi",
13270
13605
  hide_list: "Nascondi elenco",
13606
+ show_list: "Mostra elenco",
13271
13607
  unsaved_local_changes: "Modifiche locali non salvate",
13272
13608
  unsaved_local_changes_description: "Questo documento è stato modificato localmente e ha modifiche non salvate. Queste modifiche locali andranno perse se non le applichi.",
13273
13609
  preview_changes: "Anteprima modifiche",
@@ -14106,7 +14442,7 @@ var hi = {
14106
14442
  group_by: "इसके द्वारा समूहित करें",
14107
14443
  initialize_kanban_order: "कानबन क्रम प्रारंभ करें",
14108
14444
  copy_id: "ID कॉपी करें",
14109
- add_specific: "विशिष्ट {{name}} जोड़ें",
14445
+ add_specific: "{{name}} जोड़ें",
14110
14446
  select_specific: "विशिष्ट {{name}} चुनें",
14111
14447
  select_from: "{{name}} से चुनें",
14112
14448
  done: "हो गया",
@@ -14114,6 +14450,7 @@ var hi = {
14114
14450
  column_cannot_be_edited: "इस कॉलम को सीधे संपादित नहीं किया जा सकता",
14115
14451
  close: "बंद करें",
14116
14452
  hide_list: "सूची छिपाएँ",
14453
+ show_list: "सूची दिखाएँ",
14117
14454
  unsaved_local_changes: "सहेजे नहीं गए स्थानीय परिवर्तन",
14118
14455
  unsaved_local_changes_description: "इस दस्तावेज़ को स्थानीय स्तर पर संपादित किया गया था और इसमें सहेजे नहीं गए परिवर्तन हैं। यदि आप इन्हें लागू नहीं करते हैं तो ये स्थानीय परिवर्तन खो जाएंगे।",
14119
14456
  preview_changes: "परिवर्तनों का पूर्वावलोकन करें",
@@ -14965,6 +15302,7 @@ var pt = {
14965
15302
  column_cannot_be_edited: "Esta coluna não pode ser editada diretamente",
14966
15303
  close: "Fechar",
14967
15304
  hide_list: "Ocultar lista",
15305
+ show_list: "Mostrar lista",
14968
15306
  unsaved_local_changes: "Alterações locais não guardadas",
14969
15307
  unsaved_local_changes_description: "Este documento foi editado localmente e tem alterações não guardadas. Estas alterações locais serão perdidas se não as aplicar.",
14970
15308
  preview_changes: "Pré-visualizar alterações",
@@ -15840,6 +16178,11 @@ function Rebase(props) {
15840
16178
  if (new Set(keys).size !== keys.length) console.error("Duplicate plugin keys detected:", keys.filter((k, i) => keys.indexOf(k) !== i));
15841
16179
  }
15842
16180
  const resolvedSlots = useMemo(() => [...directSlots, ...(plugins ?? []).flatMap((p) => p.slots ?? [])], [directSlots, plugins]);
16181
+ useEffect(() => {
16182
+ const dead = new Set(UNRENDERED_SLOTS);
16183
+ const registered = new Set(resolvedSlots.map((s) => s.slot).filter((slot) => dead.has(slot)));
16184
+ for (const slot of registered) console.warn(`[Rebase] A component is registered for the "${slot}" slot, which this build declares but renders nowhere — nothing will appear. This is a gap in Rebase, not in your plugin.`);
16185
+ }, [resolvedSlots]);
15843
16186
  const clientAuthController = useAuthSubscription(authControllerProp ? void 0 : client?.auth);
15844
16187
  const authController = authControllerProp ?? clientAuthController;
15845
16188
  const normalizedDataSources = useMemo(() => {
@@ -16201,7 +16544,9 @@ function useRebaseAuthController(props = {}) {
16201
16544
  });
16202
16545
  auth.getAuthConfig().then((config) => {
16203
16546
  if (isMountedRef.current) setAuthConfig(config);
16204
- }).catch(() => {});
16547
+ }).catch((e) => {
16548
+ console.warn("[Rebase] Could not load the backend auth configuration; the login view will fall back to defaults.", e);
16549
+ });
16205
16550
  const unsubscribe = auth.onAuthStateChange(syncState);
16206
16551
  return () => {
16207
16552
  isMountedRef.current = false;
@@ -16636,6 +16981,21 @@ function createFormexStub(values) {
16636
16981
  //#endregion
16637
16982
  //#region src/util/entity_cache.ts
16638
16983
  var LOCAL_STORAGE_PREFIX = "entity_cache::";
16984
+ /**
16985
+ * The in-session handoff channel: an edit in flight, parked here by the layout
16986
+ * that is being replaced and picked up by the one taking over — the split's
16987
+ * "hide list", full screen's "show list", the side panel's "open full screen".
16988
+ * A record loaded from it opens *dirty*, because that is what it is for.
16989
+ *
16990
+ * Deliberately **not** hydrated from `sessionStorage`, and it is the one thing
16991
+ * here that isn't. The persisted half of this file is the local-changes backup:
16992
+ * a draft that outlives a reload, offered back through the banner or applied on
16993
+ * open, according to the collection's `localChangesBackup`. Loading those
16994
+ * drafts into this map on startup made every one of them look like a handoff —
16995
+ * the first visit to the record after a reload silently opened dirty carrying a
16996
+ * draft the user had walked away from, and the banner that exists to ask about
16997
+ * it never appeared.
16998
+ */
16639
16999
  var entityCache = /* @__PURE__ */ new Map();
16640
17000
  var isSessionStorageAvailable = typeof sessionStorage !== "undefined";
16641
17001
  function customReplacer(key) {
@@ -16690,25 +17050,12 @@ function customReviver(_key, value) {
16690
17050
  }
16691
17051
  return value;
16692
17052
  }
16693
- if (isSessionStorageAvailable) try {
16694
- for (let i = 0; i < sessionStorage.length; i++) {
16695
- const fullKey = sessionStorage.key(i);
16696
- if (fullKey && fullKey.startsWith(LOCAL_STORAGE_PREFIX)) {
16697
- const path = fullKey.substring(14);
16698
- const entityString = sessionStorage.getItem(fullKey);
16699
- if (entityString) try {
16700
- const entity = JSON.parse(entityString, customReviver);
16701
- entityCache.set(path, entity);
16702
- } catch (parseError) {
16703
- console.error(`Failed to parse entity for path "${path}" from sessionStorage:`, parseError);
16704
- }
16705
- }
16706
- }
16707
- } catch (error) {
16708
- console.error("Error accessing sessionStorage during initialization:", error);
16709
- }
16710
17053
  /**
16711
- * Saves data to the in-memory cache and persists it individually in `sessionStorage`.
17054
+ * Saves data to the local-changes backup in `sessionStorage`.
17055
+ *
17056
+ * The backup only — a draft that survives a reload is not an edit in flight.
17057
+ * {@link saveEntityToMemoryCache} is the other channel; see {@link entityCache}
17058
+ * for what separates them.
16712
17059
  * @param path - The unique path/key for the data.
16713
17060
  * @param data - The data to cache and persist.
16714
17061
  */
@@ -16721,9 +17068,18 @@ function saveEntityToCache(path, data) {
16721
17068
  console.error(`Failed to save entity for path "${path}" to sessionStorage:`, error);
16722
17069
  }
16723
17070
  }
17071
+ /**
17072
+ * Consumes a handoff. The channel is one-shot: whoever picks an edit up owns it
17073
+ * from then on, and an edit left behind here is one that reopens a record dirty
17074
+ * long after the user stopped looking at it.
17075
+ */
16724
17076
  function removeEntityFromMemoryCache(path) {
16725
17077
  entityCache.delete(path);
16726
17078
  }
17079
+ /**
17080
+ * Parks an edit in flight for the layout about to take over. See
17081
+ * {@link entityCache}: this is the handoff, not the backup.
17082
+ */
16727
17083
  function saveEntityToMemoryCache(path, data) {
16728
17084
  entityCache.set(path, data);
16729
17085
  }
@@ -16731,8 +17087,11 @@ function getEntityFromMemoryCache(path) {
16731
17087
  return entityCache.get(path);
16732
17088
  }
16733
17089
  /**
16734
- * Retrieves a entity from the in-memory cache or `sessionStorage`.
16735
- * If the entity is not in the cache but exists in `sessionStorage`, it loads it into the cache.
17090
+ * Retrieves a entity from the local-changes backup in `sessionStorage`.
17091
+ *
17092
+ * The backup only — the handoff channel is read through
17093
+ * {@link getEntityFromMemoryCache}, and the two mean different things to the
17094
+ * form that receives them.
16736
17095
  * @param path - The unique path/key for the entity.
16737
17096
  * @returns The cached entity or `undefined` if not found.
16738
17097
  */
@@ -17359,8 +17718,82 @@ function getAdminEntityChildViews(collection) {
17359
17718
  }));
17360
17719
  }
17361
17720
  //#endregion
17721
+ //#region src/collections/entity-display.ts
17722
+ /**
17723
+ * Collections that have already warned about a deprecated field, so a list of
17724
+ * fifty rows produces one line in the console rather than fifty.
17725
+ */
17726
+ var deprecationWarned = /* @__PURE__ */ new Set();
17727
+ function collectionId(collection) {
17728
+ return collection.slug ?? collection.name ?? "collection";
17729
+ }
17730
+ function warnOnce(id, message) {
17731
+ const key = `${id}:${message}`;
17732
+ if (deprecationWarned.has(key)) return;
17733
+ deprecationWarned.add(key);
17734
+ console.warn(message);
17735
+ }
17736
+ /**
17737
+ * What the collection declares for a role, before deciding which form it is.
17738
+ *
17739
+ * `display.title` wins over the deprecated `titleProperty`: a collection setting
17740
+ * both is mid-migration, and the new field is the one it means.
17741
+ */
17742
+ function getDeclaredSource(collection, role) {
17743
+ const declared = collection.display?.[role];
17744
+ if (declared !== void 0) return declared;
17745
+ if (role === "title" && collection.titleProperty) {
17746
+ const id = collectionId(collection);
17747
+ warnOnce(id, `[rebase] Collection "${id}" uses admin.titleProperty, which is deprecated. Move it to admin.display.title — the same string works there, and display.title also accepts a resolver for a title the record does not carry.`);
17748
+ return collection.titleProperty;
17749
+ }
17750
+ }
17751
+ /**
17752
+ * The property path a role is declared to read, when it is declared as a path.
17753
+ *
17754
+ * Returns `undefined` for a role filled by a resolver — a resolver has no key —
17755
+ * and for a role the collection says nothing about, which is then derived.
17756
+ *
17757
+ * @group Collections
17758
+ */
17759
+ function getDisplayPropertyKey(collection, role) {
17760
+ const declared = getDeclaredSource(collection, role);
17761
+ return typeof declared === "string" ? declared : void 0;
17762
+ }
17763
+ /**
17764
+ * The resolver a role is declared to use, when it is declared as one.
17765
+ *
17766
+ * @group Collections
17767
+ */
17768
+ function getDisplayResolver(collection, role) {
17769
+ const declared = getDeclaredSource(collection, role);
17770
+ return typeof declared === "function" ? declared : void 0;
17771
+ }
17772
+ /**
17773
+ * True when the collection states this role at all, in either form.
17774
+ *
17775
+ * The derivation is a guess about what a collection probably means; a statement
17776
+ * outranks it, and the heuristics that look for "the first enum" or "the leading
17777
+ * relation" have to stand down when one exists.
17778
+ *
17779
+ * @group Collections
17780
+ */
17781
+ function hasDeclaredDisplay(collection, role) {
17782
+ return getDeclaredSource(collection, role) !== void 0;
17783
+ }
17784
+ //#endregion
17362
17785
  //#region src/collections/entity_image_preview.ts
17786
+ /**
17787
+ * The property that fills a record's image slot.
17788
+ *
17789
+ * `admin.display.image` first, then six fallbacks in descending confidence —
17790
+ * the first image-typed storage property, an array of them, a URL rendered as an
17791
+ * image, and so on. The ladder stays for collections that say nothing; a
17792
+ * collection that names its picture is not guessed at.
17793
+ */
17363
17794
  function getEntityImagePreviewPropertyKey(collection) {
17795
+ const declared = getDisplayPropertyKey(collection, "image");
17796
+ if (declared) return declared;
17364
17797
  for (const key in collection.properties) {
17365
17798
  const property = collection.properties[key];
17366
17799
  if (property.type === "string" && property.storage?.acceptedFiles?.includes("image/*")) return key;
@@ -17387,6 +17820,142 @@ function getEntityImagePreviewPropertyKey(collection) {
17387
17820
  }
17388
17821
  }
17389
17822
  //#endregion
17823
+ //#region src/collections/entity-display-cache.ts
17824
+ /** The identity of one role of one record, as a cache key. */
17825
+ function entityDisplayKey(path, entityId, role) {
17826
+ return `${role} ${path} ${entityId ?? ""}`;
17827
+ }
17828
+ var EntityDisplayCache = class {
17829
+ entries = /* @__PURE__ */ new Map();
17830
+ listeners = /* @__PURE__ */ new Set();
17831
+ /**
17832
+ * The resolved value, or `undefined` when this pair has not been resolved
17833
+ * yet. `null` is a resolved absence, and the two must stay distinct: a
17834
+ * caller that reads "not yet" as "nothing" flickers its fallback in on every
17835
+ * mount.
17836
+ */
17837
+ peek(key) {
17838
+ const entry = this.entries.get(key);
17839
+ if (!entry || "promise" in entry) return void 0;
17840
+ return entry.value;
17841
+ }
17842
+ /** True while a resolution for this pair is in flight. */
17843
+ isLoading(key) {
17844
+ const entry = this.entries.get(key);
17845
+ return Boolean(entry && "promise" in entry);
17846
+ }
17847
+ /**
17848
+ * Resolve once per record and role. Concurrent callers share the first
17849
+ * call's promise; later callers get the cached value with no promise at all.
17850
+ *
17851
+ * A resolver that throws is recorded as "nothing" rather than retried: the
17852
+ * alternative is every render re-running a call that just failed. And it is
17853
+ * reported here, which is a correction.
17854
+ *
17855
+ * It used to say "the caller that saw the rejection is the one that logs
17856
+ * it", and `useEntityDisplay` duly attached a `.catch()` that warned. But
17857
+ * both failure paths below swallow and return a *resolved* promise, so that
17858
+ * catch could never run — the two halves each did the reasonable thing and
17859
+ * between them the log was unreachable. A resolver that blew up produced a
17860
+ * blank chip and total silence, which is the failure mode
17861
+ * `EntityDisplayResolver`'s own contract ("treated as `undefined` and logged
17862
+ * once") exists to rule out.
17863
+ *
17864
+ * Reporting belongs here for the reason the caller could not do it: this is
17865
+ * the one place that runs exactly once per key, so "once" is a property of
17866
+ * the code rather than a hope about how many components mount.
17867
+ */
17868
+ resolve(key, resolver) {
17869
+ const entry = this.entries.get(key);
17870
+ if (entry) return "promise" in entry ? entry.promise : Promise.resolve(entry.value);
17871
+ let produced;
17872
+ try {
17873
+ produced = resolver();
17874
+ } catch (error) {
17875
+ this.fail(key, error);
17876
+ return Promise.resolve(null);
17877
+ }
17878
+ if (!isPromise(produced)) {
17879
+ const value = normalize(produced);
17880
+ this.set(key, value);
17881
+ return Promise.resolve(value);
17882
+ }
17883
+ const promise = produced.then((resolved) => {
17884
+ const value = normalize(resolved);
17885
+ this.set(key, value);
17886
+ return value;
17887
+ }).catch((error) => {
17888
+ this.fail(key, error);
17889
+ return null;
17890
+ });
17891
+ this.entries.set(key, { promise });
17892
+ return promise;
17893
+ }
17894
+ /**
17895
+ * Drop what is known about a record, so the next ask resolves again. Called
17896
+ * after a write: the row that just saved may be called something else now.
17897
+ */
17898
+ invalidate(path, entityId) {
17899
+ const suffix = ` ${path} ${entityId ?? ""}`;
17900
+ let changed = false;
17901
+ for (const key of [...this.entries.keys()]) if (entityId === void 0 ? key.includes(` ${path} `) || key.endsWith(` ${path} `) : key.endsWith(suffix)) {
17902
+ this.entries.delete(key);
17903
+ changed = true;
17904
+ }
17905
+ if (changed) this.emit();
17906
+ }
17907
+ /** Drop everything. The user signed out, or the app swapped datasource. */
17908
+ clear() {
17909
+ if (this.entries.size === 0) return;
17910
+ this.entries.clear();
17911
+ this.emit();
17912
+ }
17913
+ subscribe(listener) {
17914
+ this.listeners.add(listener);
17915
+ return () => {
17916
+ this.listeners.delete(listener);
17917
+ };
17918
+ }
17919
+ set(key, value) {
17920
+ this.entries.set(key, { value });
17921
+ this.emit();
17922
+ }
17923
+ /**
17924
+ * Record a failed resolution as "nothing", and say so once.
17925
+ *
17926
+ * The key is the message: it is `<role> <path> <id>`, which is exactly what a
17927
+ * reader needs to find the resolver that blew up. A warning with no key would
17928
+ * tell them a display resolver failed somewhere in a list of fifty rows.
17929
+ *
17930
+ * `console.warn` rather than a thrown error, because this runs while a row is
17931
+ * rendering: the contract is that a title which cannot be fetched must not
17932
+ * take down the row that shows it.
17933
+ */
17934
+ fail(key, error) {
17935
+ console.warn(`[rebase] Could not resolve display value for ${key}:`, error);
17936
+ this.set(key, null);
17937
+ }
17938
+ emit() {
17939
+ for (const listener of [...this.listeners]) listener();
17940
+ }
17941
+ };
17942
+ /**
17943
+ * Empty strings and empty arrays are absences, not values — a title of `" "`
17944
+ * would otherwise beat the derived one and render as a blank heading.
17945
+ */
17946
+ function normalize(value) {
17947
+ if (value === void 0 || value === null) return null;
17948
+ if (typeof value === "string") {
17949
+ const trimmed = value.trim();
17950
+ return trimmed.length > 0 ? trimmed : null;
17951
+ }
17952
+ if (Array.isArray(value)) return value.length > 0 ? value : null;
17953
+ return value;
17954
+ }
17955
+ function isPromise(value) {
17956
+ return typeof value?.then === "function";
17957
+ }
17958
+ //#endregion
17390
17959
  //#region src/collections/filter-operator-resolution.ts
17391
17960
  /**
17392
17961
  * Default operators offered per property type, before engine capabilities and
@@ -17497,115 +18066,6 @@ function resolveFilterOperators({ property, isArray, engine }) {
17497
18066
  return typeDefaults.filter((op) => engineOps.has(op) && (narrowingSet === void 0 || narrowingSet.has(op)));
17498
18067
  }
17499
18068
  //#endregion
17500
- //#region src/collections/property_presentation.ts
17501
- function isReadOnly(property) {
17502
- if (property.admin?.readOnly) return true;
17503
- if (property.type === "date") {
17504
- if (property.autoValue) return true;
17505
- }
17506
- if (property.type === "reference") return !property.path && !("Field" in (property.admin || {}) && property.admin?.Field);
17507
- return false;
17508
- }
17509
- function isHidden(property) {
17510
- return typeof property.admin?.disabled === "object" && Boolean(property.admin?.disabled.hidden);
17511
- }
17512
- function applyPropertyConditions(property, context) {
17513
- const { conditions } = property;
17514
- if (!conditions) return property;
17515
- const result = { ...property };
17516
- if (conditions.disabled) {
17517
- if (evaluateCondition(conditions.disabled, context)) {
17518
- result.admin = result.admin || {};
17519
- result.admin.disabled = {
17520
- clearOnDisabled: conditions.clearOnDisabled ?? false,
17521
- disabledMessage: conditions.disabledMessage,
17522
- hidden: false
17523
- };
17524
- }
17525
- }
17526
- if (conditions.hidden) {
17527
- if (evaluateCondition(conditions.hidden, context)) {
17528
- result.admin = result.admin || {};
17529
- result.admin.disabled = {
17530
- ...typeof result.admin?.disabled === "object" ? result.admin.disabled : {},
17531
- hidden: true,
17532
- clearOnDisabled: conditions.clearOnDisabled ?? false
17533
- };
17534
- }
17535
- }
17536
- if (conditions.readOnly) {
17537
- if (evaluateCondition(conditions.readOnly, context)) {
17538
- result.admin = result.admin || {};
17539
- result.admin.readOnly = true;
17540
- }
17541
- }
17542
- if (conditions.required !== void 0) {
17543
- const isRequired = evaluateCondition(conditions.required, context);
17544
- result.validation = {
17545
- ...result.validation,
17546
- required: isRequired,
17547
- requiredMessage: conditions.requiredMessage
17548
- };
17549
- }
17550
- if (context.isNew && conditions.defaultValue !== void 0) result.defaultValue = evaluateCondition(conditions.defaultValue, context);
17551
- if ("enum" in result && result.enum && (conditions.enumConditions || conditions.allowedEnumValues || conditions.excludedEnumValues)) result.enum = applyEnumConditions(result.enum, conditions, context);
17552
- if (result.type === "reference") {
17553
- if (conditions.referencePath) result.path = evaluateCondition(conditions.referencePath, context);
17554
- if (conditions.referenceFilter) {
17555
- result.admin = result.admin || {};
17556
- result.admin.fixedFilter = evaluateCondition(conditions.referenceFilter, context);
17557
- }
17558
- }
17559
- if (result.type === "array") {
17560
- if (conditions.canAddElements !== void 0) {
17561
- result.admin = result.admin || {};
17562
- result.admin.canAddElements = evaluateCondition(conditions.canAddElements, context);
17563
- }
17564
- if (conditions.sortable !== void 0) {
17565
- result.admin = result.admin || {};
17566
- result.admin.sortable = evaluateCondition(conditions.sortable, context);
17567
- }
17568
- }
17569
- return result;
17570
- }
17571
- /**
17572
- * Convert an object with numeric keys back to an array.
17573
- * Firestore stores arrays as {"0": "a", "1": "b"} to avoid nested arrays.
17574
- */
17575
- function objectToArray(obj) {
17576
- if (Array.isArray(obj)) return obj.map(String);
17577
- if (obj && typeof obj === "object") {
17578
- const keys = Object.keys(obj);
17579
- if (keys.length > 0 && keys.every((k) => !isNaN(Number(k)))) return keys.sort((a, b) => Number(a) - Number(b)).map((k) => obj[k]).filter((v) => typeof v === "string" || typeof v === "number").map(String);
17580
- }
17581
- return [];
17582
- }
17583
- /**
17584
- * Apply enum-specific conditions to filter and modify enum values.
17585
- */
17586
- function applyEnumConditions(enumValues, conditions, context) {
17587
- let result = [...enumValues];
17588
- if (conditions.allowedEnumValues) {
17589
- const allowedArray = objectToArray(evaluateCondition(conditions.allowedEnumValues, context));
17590
- if (allowedArray.length > 0) result = result.filter((ev) => allowedArray.includes(String(ev.id)));
17591
- }
17592
- if (conditions.excludedEnumValues) {
17593
- const excludedArray = objectToArray(evaluateCondition(conditions.excludedEnumValues, context));
17594
- if (excludedArray.length > 0) result = result.filter((ev) => !excludedArray.includes(String(ev.id)));
17595
- }
17596
- if (conditions.enumConditions) result = result.map((ev) => {
17597
- const evConditions = conditions.enumConditions?.[ev.id];
17598
- if (!evConditions) return ev;
17599
- if (evConditions.hidden && evaluateCondition(evConditions.hidden, context)) return null;
17600
- if (evConditions.disabled && evaluateCondition(evConditions.disabled, context)) return {
17601
- ...ev,
17602
- disabled: true
17603
- };
17604
- return ev;
17605
- }).filter((ev) => ev !== null);
17606
- return result;
17607
- }
17608
- //#endregion
17609
18069
  //#region src/collections/form-layout.ts
17610
18070
  /**
17611
18071
  * Columns in the form grid.
@@ -17796,7 +18256,8 @@ function resolveFormLayout({ collection, fieldKeys, status }) {
17796
18256
  title: section.title,
17797
18257
  collapsible: section.collapsible ?? titled,
17798
18258
  collapsed: titled ? Boolean(section.collapsed) : false,
17799
- fields
18259
+ fields,
18260
+ readVariant: section.readVariant
17800
18261
  });
17801
18262
  }
17802
18263
  const leftovers = fieldKeys.map((key) => available.get(key)).filter((f) => Boolean(f));
@@ -18079,18 +18540,6 @@ function isHidden$1(property) {
18079
18540
  return Boolean(property.admin?.hideFromCollection);
18080
18541
  }
18081
18542
  /**
18082
- * File-storage backed content (single image, array of images, generic upload…).
18083
- * Rendered by the dedicated image slot, so never a title.
18084
- */
18085
- function isStorageProperty(property) {
18086
- if (property.type === "string" && (property.storage || property.admin?.urlPreview === "image")) return true;
18087
- if (property.type === "array" && property.of && !Array.isArray(property.of)) {
18088
- const inner = property.of;
18089
- if (inner.type === "string" && (inner.storage || inner.admin?.urlPreview === "image")) return true;
18090
- }
18091
- return false;
18092
- }
18093
- /**
18094
18543
  * Every column on this collection that stores a foreign key: the `localKey` of
18095
18544
  * each owning relation (declared inline on a property or in `relations[]`), the
18096
18545
  * source column of a junction, and the key a many-to-many joins on.
@@ -18170,7 +18619,8 @@ function scoreTitleCandidate(property, key, idKeys, foreignKeys) {
18170
18619
  */
18171
18620
  function getTitlePropertyCandidates(collection) {
18172
18621
  if (!collection.properties) return [];
18173
- if (collection.titleProperty && collection.properties[collection.titleProperty]) return [collection.titleProperty];
18622
+ const declared = getDisplayPropertyKey(collection, "title");
18623
+ if (declared && getPropertyInPath(collection.properties, declared)) return [declared];
18174
18624
  const idKeys = new Set(getPrimaryKeys(collection));
18175
18625
  const foreignKeys = getForeignKeyColumns(collection);
18176
18626
  const explicitOrder = collection.propertiesOrder;
@@ -18213,7 +18663,7 @@ function getTitlePropertyCandidates(collection) {
18213
18663
  */
18214
18664
  function getLeadingRelationTitleKey(collection) {
18215
18665
  if (!collection.properties) return void 0;
18216
- if (collection.titleProperty) return void 0;
18666
+ if (hasDeclaredDisplay(collection, "title")) return void 0;
18217
18667
  const idKeys = new Set(getPrimaryKeys(collection));
18218
18668
  const foreignKeys = getForeignKeyColumns(collection);
18219
18669
  const order = collection.propertiesOrder ?? Object.keys(collection.properties);
@@ -18274,6 +18724,6 @@ function getTitlePropertyKeyForValues(collection, values, entityId) {
18274
18724
  return candidates[0];
18275
18725
  }
18276
18726
  //#endregion
18277
- export { AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_API_PATH, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, SIDE_PANEL_DEFAULT_WIDTH, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, addInitialSlash, apiBaseOf, applyPropertyConditions, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, deriveSpan, en, es, fetchAuthConfig, fillRows, flattenKeys, getAdminEntityChildViews, getAdminSubcollections, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getLastSegment, getLeadingRelationTitleKey, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, iconsSearch, isAuditTimestamp, isEnumValueDisabled, isFilterableRelation, isHidden, isIdPropertyEditable, isReadOnly, isSchemaDriftError, looksLikeIdentifierValue, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveCollectionPathIds, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, resolveFormLayout, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCapabilities, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
18727
+ export { AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_API_PATH, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, EntityDisplayCache, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, SIDE_PANEL_DEFAULT_WIDTH, STUDIO_NAVIGATION_GROUPS, SUMMARY_RANK, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, addInitialSlash, apiBaseOf, applyPropertyConditions, buildCollapsedDefaults, buildEnumLabel, canSummariseProperty, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, deriveSpan, en, entityDisplayKey, es, fetchAuthConfig, fillRows, flattenKeys, getAdminEntityChildViews, getAdminSubcollections, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getDisplayPropertyKey, getDisplayResolver, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getLastSegment, getLeadingRelationTitleKey, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getPropertyInPath, getRedundantChildViewColumnIds, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, hasDeclaredDisplay, iconsSearch, isAuditTimestamp, isDisabled, isEnumValueDisabled, isFilterableRelation, isHidden, isIdPropertyEditable, isLongTextProperty, isReadOnly, isSchemaDriftError, isStorageProperty, looksLikeIdentifierValue, populateFetchCache, rankSummaryProperty, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialSlash, removeTrailingSlash, resolveCollectionPathIds, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, resolveFormLayout, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCapabilities, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
18278
18728
 
18279
18729
  //# sourceMappingURL=index.es.js.map