@elaraai/e3-ui-components 1.0.55 → 1.0.57

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.
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import { EastTypeType } from "@elaraai/east/internal";
23
23
  import { DataBindModeType, DataBindPrimitives, bindPlatformFn, FuncBindPrimitives, FuncErrorType, FuncStatusType, funcBindPlatformFn, RecordBindPrimitives, recordBindPlatformFn, Diff, Ontology, FuncBindHandleType, Experiment, decisionBindPlatformFn, DecisionBindPrimitives, DecisionConstraintType, DiffBindingType, CommitStateType, VerdictType, AnswerType, judgementInputType, DecisionType, DecisionQueue, DecisionJournal, decodeManifest } from "@elaraai/e3-ui/internal";
24
24
  import { registerReactiveTracker, registerPlatformImplementation, StateRuntime, getRegisteredPlatformImplementations, boundSliceConfig } from "@elaraai/east-ui-components/platform";
25
25
  import { TreePathType, DatasetStatusType } from "@elaraai/e3-types";
26
- import { workspaceStatus, datasetListAt, datasetList, dataflowExecuteLaunch, datasetSet, datasetGet, ApiError, workspaceFunctionCall, workspaceFunctionList, workspaceRecordHistory, workspaceRecordMutate, workspaceRecordDescribe, repoCreate, repoGc, repoGcStart, repoGcStatus, repoList, repoRemove, repoStatus, packageExport, packageGet, packageImport, packageList, packageRemove, workspaceCreate, workspaceDeploy, workspaceExport, workspaceGet, workspaceList, workspaceRemove, datasetGetStatus, datasetListRecursive, datasetListRecursivePaths, datasetListWithStatus, taskExecutionList, taskGet, taskList, dataflowCancel, dataflowExecute, dataflowExecutePoll, dataflowGraph, taskLogs } from "@elaraai/e3-api-client";
26
+ import { workspaceStatus, datasetListAt, datasetList, dataflowExecuteLaunch, datasetSet, datasetGet, ApiError, workspaceFunctionCall, workspaceFunctionList, workspaceRecordHistory, workspaceRecordMutate, workspaceRecordDescribe, repoCreate, repoGc, repoGcStart, repoGcStatus, repoList, repoRemove, repoStatus, packageExport, packageGet, packageImport, packageList, packageRemove, workspaceCreate, workspaceDeploy, workspaceExport, workspaceGet, workspaceList, workspaceRemove, datasetGetStatus, datasetListRecursive, datasetListRecursivePaths, datasetListWithStatus, taskExecutionList, taskGet, taskList, dataflowCancel, dataflowExecute, dataflowExecutePoll, dataflowGraph, taskLogs, datasetGetPage } from "@elaraai/e3-api-client";
27
27
  import { jsx as jsx$1, Fragment, jsxs } from "react/jsx-runtime";
28
28
  import * as React from "react";
29
29
  import React__default, { useId, useMemo, useContext, createContext, useRef, useEffect, useState, useCallback, useSyncExternalStore, memo, forwardRef, useLayoutEffect, Fragment as Fragment$1, Component } from "react";
@@ -31,7 +31,7 @@ import { Box, Flex, Text, HStack, Input, useSlotRecipe, useRecipe, VStack, useTo
31
31
  import { implementUIComponent, getSomeorUndefined, formatPredicate, SliceEditPopover, SlicePredicateBuilder, EastChakraSelect, EastChakraStringInput, EastChakraDateTimeInput, EastChakraFloatInput, EastChakraIntegerInput, formatTick, useSliceReactivity, useContainerBelow, usePersistedState, SliceRailCluster, EastChakraComponent, ClauseChip, StateImpl, NavImpl, SliceImpl, SliceApplyImpl, OverlayImpl, ClipboardImpl, DownloadImpl, ShareImpl, LoadingIcon, EastChakraValueTree, createUIStore, UIStoreProvider } from "@elaraai/east-ui-components";
32
32
  import { createPortal } from "react-dom";
33
33
  import { sliceMatches } from "@elaraai/east-ui/internal";
34
- import { QueryClient as QueryClient$1, QueryClientProvider, useMutation, useQuery, useQueryClient, keepPreviousData } from "@tanstack/react-query";
34
+ import { QueryClient as QueryClient$1, QueryClientProvider, useMutation, useQuery, keepPreviousData, useQueryClient } from "@tanstack/react-query";
35
35
  import { ValueTree } from "@elaraai/east-ui";
36
36
  import { useVirtualizer } from "@tanstack/react-virtual";
37
37
  var Subscribable = class {
@@ -63839,6 +63839,35 @@ function useDatasetDownload(apiUrl, repo, workspace, datasetPath, requestOptions
63839
63839
  URL.revokeObjectURL(url);
63840
63840
  }, [apiUrl, repo, workspace, datasetPath, pathParts, reqOpts]);
63841
63841
  }
63842
+ function useDatasetPage(apiUrl, repo, workspace, datasetPath, options2) {
63843
+ const { requestOptions, queryOverrides, enabled = true, hash: hash2, type, window: window3 } = options2;
63844
+ const reqOpts = requestOptions ?? { token: null };
63845
+ const pathParts = useMemo(
63846
+ () => (datasetPath == null ? void 0 : datasetPath.split(".").filter(Boolean).map((v3) => variant("field", v3))) ?? [],
63847
+ [datasetPath]
63848
+ );
63849
+ const windowKey = "segment" in window3 ? `s${window3.segment}` : `${window3.offset}+${window3.limit}`;
63850
+ return useQuery({
63851
+ queryKey: ["datasetPage", apiUrl, repo, workspace, datasetPath, hash2 ?? null, windowKey],
63852
+ queryFn: async () => {
63853
+ const pinned = hash2 != null ? { ...window3, hash: hash2 } : window3;
63854
+ const page = await datasetGetPage(apiUrl, repo, workspace, pathParts, pinned, reqOpts);
63855
+ const decoded = decodeBeast2For(type)(page.data);
63856
+ return {
63857
+ decoded,
63858
+ totalElements: page.totalElements,
63859
+ totalBytes: page.totalBytes,
63860
+ totalExact: page.totalExact,
63861
+ segmentCount: page.segmentCount,
63862
+ offset: page.offset,
63863
+ count: page.count
63864
+ };
63865
+ },
63866
+ enabled: enabled && !!workspace && !!datasetPath && hash2 != null,
63867
+ placeholderData: keepPreviousData,
63868
+ ...queryOverrides
63869
+ });
63870
+ }
63842
63871
  function useTaskDetails(apiUrl, repo, workspace, taskName, options2) {
63843
63872
  const reqOpts = (options2 == null ? void 0 : options2.requestOptions) ?? { token: null };
63844
63873
  return useQuery({
@@ -63982,6 +64011,158 @@ class ErrorBoundary extends Component {
63982
64011
  return this.props.children;
63983
64012
  }
63984
64013
  }
64014
+ function pagingDebug(...args) {
64015
+ try {
64016
+ if (typeof localStorage === "undefined") return;
64017
+ const flag = localStorage.getItem("e3-paging-debug");
64018
+ if (flag === null || flag === "off") return;
64019
+ } catch {
64020
+ return;
64021
+ }
64022
+ console.info("[e3-paging]", ...args);
64023
+ }
64024
+ const PAGE_SIZE = 500;
64025
+ function pageRows(type, decoded, offset) {
64026
+ if (type.type === "Array") {
64027
+ const elemType2 = type.value;
64028
+ return decoded.map((el, i) => ({
64029
+ node: ValueTree.materialize(elemType2, el),
64030
+ step: variant("index", BigInt(offset + i))
64031
+ }));
64032
+ }
64033
+ if (type.type === "Dict") {
64034
+ const { key: keyType, value: valueType } = type.value;
64035
+ const printKey = printFor(keyType);
64036
+ const stringKeys = keyType.type === "String";
64037
+ return Array.from(decoded.entries()).map(([k2, v3], i) => ({
64038
+ node: ValueTree.materialize(valueType, v3),
64039
+ step: stringKeys ? variant("key", k2) : variant("index", BigInt(offset + i)),
64040
+ label: stringKeys ? k2 : printKey(k2)
64041
+ }));
64042
+ }
64043
+ const elemType = type.value;
64044
+ return Array.from(decoded.values()).map((el, i) => ({
64045
+ node: ValueTree.materialize(elemType, el),
64046
+ step: variant("index", BigInt(offset + i))
64047
+ }));
64048
+ }
64049
+ function emptyOf(type) {
64050
+ return type.type === "Array" ? [] : type.type === "Dict" ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Set();
64051
+ }
64052
+ const PagedDatasetPreview = memo(function PagedDatasetPreview2({
64053
+ apiUrl,
64054
+ repo,
64055
+ workspace,
64056
+ path: path2,
64057
+ type,
64058
+ hash: hash2,
64059
+ sizeBytes,
64060
+ requestOptions,
64061
+ onDownload
64062
+ }) {
64063
+ const queryClient = useQueryClient();
64064
+ const [pages, setPages] = useState(/* @__PURE__ */ new Map());
64065
+ const [totals, setTotals] = useState(null);
64066
+ const [loadingCount, setLoadingCount] = useState(0);
64067
+ const [error3, setError] = useState(null);
64068
+ const inflightRef = useRef(/* @__PURE__ */ new Set());
64069
+ useEffect(() => {
64070
+ pagingDebug(`reset (path=${path2}, hash=${hash2.slice(0, 8)})`);
64071
+ setPages(/* @__PURE__ */ new Map());
64072
+ setTotals(null);
64073
+ setError(null);
64074
+ inflightRef.current.clear();
64075
+ }, [path2, hash2]);
64076
+ const loadPage = useCallback((pageIdx) => {
64077
+ if (inflightRef.current.has(pageIdx)) return;
64078
+ inflightRef.current.add(pageIdx);
64079
+ setLoadingCount((n2) => n2 + 1);
64080
+ pagingDebug(`fetch p${pageIdx} (offset=${pageIdx * PAGE_SIZE}, limit=${PAGE_SIZE}, hash=${hash2.slice(0, 8)})`);
64081
+ const pathParts = path2.split(".").filter(Boolean).map((v3) => variant("field", v3));
64082
+ const reqOpts = requestOptions ?? { token: null };
64083
+ queryClient.fetchQuery({
64084
+ queryKey: ["datasetPage", apiUrl, repo, workspace, path2, hash2, `p${pageIdx}`],
64085
+ // Hash-pinned: the URL is a pure function of the bytes, so any
64086
+ // HTTP cache between here and the server can hold it immutably.
64087
+ queryFn: () => datasetGetPage(apiUrl, repo, workspace, pathParts, { offset: pageIdx * PAGE_SIZE, limit: PAGE_SIZE, hash: hash2 }, reqOpts),
64088
+ staleTime: Infinity
64089
+ // pages of one content hash are immutable
64090
+ }).then((page) => {
64091
+ const decoded = decodeBeast2For(type)(page.data);
64092
+ const rows = pageRows(type, decoded, page.offset);
64093
+ pagingDebug(
64094
+ `p${pageIdx} ok: count=${page.count} rows=${rows.length} offset=${page.offset} total=${page.totalElements} segs=${page.segmentCount}`,
64095
+ rows.length > 0 ? `first=${JSON.stringify(rows[0].label ?? "(derived)")}` : "(empty)"
64096
+ );
64097
+ if (page.count < PAGE_SIZE && page.offset + page.count < page.totalElements) {
64098
+ pagingDebug(`p${pageIdx} SHORT page: server clamped ${PAGE_SIZE} → ${page.count}`);
64099
+ }
64100
+ setTotals({ elements: page.totalElements, bytes: page.totalBytes });
64101
+ setPages((prev2) => new Map(prev2).set(pageIdx, rows));
64102
+ }).catch((err) => {
64103
+ pagingDebug(`p${pageIdx} FAILED:`, err);
64104
+ setError(err instanceof Error ? err : new Error(String(err)));
64105
+ }).finally(() => {
64106
+ inflightRef.current.delete(pageIdx);
64107
+ setLoadingCount((n2) => n2 - 1);
64108
+ });
64109
+ }, [queryClient, apiUrl, repo, workspace, path2, hash2, requestOptions, type]);
64110
+ useEffect(() => {
64111
+ if (totals === null && error3 === null) loadPage(0);
64112
+ }, [totals, error3, loadPage]);
64113
+ const onNeedRows = useCallback((startRow, endRow) => {
64114
+ const first2 = Math.max(0, Math.floor(startRow / PAGE_SIZE));
64115
+ const last2 = Math.max(first2, Math.ceil(endRow / PAGE_SIZE) - 1);
64116
+ const missing = [];
64117
+ for (let p2 = first2; p2 <= last2; p2++) {
64118
+ if (!pages.has(p2)) missing.push(p2);
64119
+ }
64120
+ pagingDebug(`need rows [${startRow}, ${endRow}) → pages ${first2}..${last2}, missing=[${missing.join(",")}], inflight=[${[...inflightRef.current].join(",")}]`);
64121
+ for (const p2 of missing) loadPage(p2);
64122
+ }, [pages, loadPage]);
64123
+ const treeValue = useMemo(() => ({
64124
+ root: ValueTree.materialize(type, emptyOf(type)),
64125
+ onEdit: none$1,
64126
+ onInsert: none$1,
64127
+ onRemove: none$1,
64128
+ onTag: none$1,
64129
+ style: some({ height: some("100%"), maxHeight: none$1, openDepth: none$1, toolbar: some(true) })
64130
+ }), [type]);
64131
+ const paging = useMemo(() => totals === null ? null : {
64132
+ totalRows: totals.elements,
64133
+ pageSize: PAGE_SIZE,
64134
+ pages,
64135
+ onNeedRows
64136
+ }, [totals, pages, onNeedRows]);
64137
+ if (error3 !== null) {
64138
+ if (error3 instanceof ApiError && (error3.code === "dataset_too_large_unindexed" || error3.code === "dataset_too_large")) {
64139
+ return /* @__PURE__ */ jsxs(Flex, { height: "100%", direction: "column", align: "center", justify: "center", gap: 3, p: 6, children: [
64140
+ /* @__PURE__ */ jsx$1(Text, { fontSize: "lg", color: "fg.warning", fontWeight: "bold", children: "Too large to page" }),
64141
+ /* @__PURE__ */ jsx$1(Text, { color: "fg.muted", fontSize: "sm", textAlign: "center", maxW: "32rem", children: typeof error3.details === "string" ? error3.details : "This dataset cannot be paged server-side." }),
64142
+ /* @__PURE__ */ jsx$1(DownloadButton, { onClick: onDownload, label: "Download value" })
64143
+ ] });
64144
+ }
64145
+ return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "error", title: "Paged load failed", message: error3.message });
64146
+ }
64147
+ if (paging === null || totals === null) {
64148
+ return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "loading", title: "Loading..." });
64149
+ }
64150
+ const itemNoun = type.type === "Dict" ? "entries" : "items";
64151
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", height: "100%", overflow: "hidden", children: [
64152
+ /* @__PURE__ */ jsxs(Flex, { px: 4, py: 2, gap: 2, align: "center", flexShrink: 0, borderBottom: "1px solid", borderColor: "border.subtle", children: [
64153
+ /* @__PURE__ */ jsxs(Text, { fontSize: "xs", color: "fg.muted", whiteSpace: "nowrap", children: [
64154
+ totals.elements.toLocaleString(),
64155
+ " ",
64156
+ itemNoun,
64157
+ " · ",
64158
+ formatSize(totals.bytes > 0 ? totals.bytes : sizeBytes)
64159
+ ] }),
64160
+ loadingCount > 0 && /* @__PURE__ */ jsx$1(Text, { fontSize: "xs", color: "fg.muted", children: "Loading…" }),
64161
+ /* @__PURE__ */ jsx$1(Flex, { flex: 1, justify: "flex-end", children: /* @__PURE__ */ jsx$1(DownloadButton, { onClick: onDownload }) })
64162
+ ] }),
64163
+ /* @__PURE__ */ jsx$1(Box, { flex: 1, minHeight: 0, overflow: "hidden", children: /* @__PURE__ */ jsx$1(EastChakraValueTree, { value: treeValue, storageKey: `${path2}:page`, paging }) })
64164
+ ] });
64165
+ }, (prev2, next2) => prev2.path === next2.path && prev2.workspace === next2.workspace && prev2.hash === next2.hash && prev2.sizeBytes === next2.sizeBytes);
63985
64166
  const DEFAULT_SIZE_LIMIT = 200 * 1024;
63986
64167
  function formatSize(bytes) {
63987
64168
  if (bytes < 1024) return `${bytes} B`;
@@ -64054,7 +64235,7 @@ const DatasetPreview = memo(function DatasetPreview2({
64054
64235
  onRemove: some((p2) => write(ValueTree.applyEdit(type, decoded, p2, { kind: "remove" }))),
64055
64236
  onTag: some((p2, tag) => write(ValueTree.applyEdit(type, decoded, p2, { kind: "tag", tag })))
64056
64237
  } : { onEdit: none$1, onInsert: none$1, onRemove: none$1, onTag: none$1 };
64057
- return { root: root2, ...wire, style: some({ height: some("100%"), maxHeight: none$1 }) };
64238
+ return { root: root2, ...wire, style: some({ height: some("100%"), maxHeight: none$1, openDepth: none$1, toolbar: some(true) }) };
64058
64239
  }, [type, decoded, editable, write]);
64059
64240
  if (statusQuery.isLoading) return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "loading", title: "Loading..." });
64060
64241
  if (statusQuery.error) {
@@ -64064,6 +64245,23 @@ const DatasetPreview = memo(function DatasetPreview2({
64064
64245
  if (!status) return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "info", title: "No status" });
64065
64246
  if (!hasValue) return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "info", title: "No data available", message: "Waiting for a value to be set" });
64066
64247
  if (isOversized) {
64248
+ const pageable = type !== void 0 && (type.type === "Array" || type.type === "Set" || type.type === "Dict");
64249
+ if (pageable && workspace != null && path2 != null && status.hash != null) {
64250
+ return /* @__PURE__ */ jsx$1(
64251
+ PagedDatasetPreview,
64252
+ {
64253
+ apiUrl,
64254
+ repo,
64255
+ workspace,
64256
+ path: path2,
64257
+ type,
64258
+ hash: status.hash,
64259
+ sizeBytes,
64260
+ ...requestOptions != null && { requestOptions },
64261
+ onDownload: download
64262
+ }
64263
+ );
64264
+ }
64067
64265
  return /* @__PURE__ */ jsxs(Flex, { height: "100%", direction: "column", align: "center", justify: "center", layerStyle: "banner.stale", borderRadius: "0", gap: 3, p: 6, children: [
64068
64266
  /* @__PURE__ */ jsx$1(Text, { fontSize: "lg", color: "fg.warning", fontWeight: "bold", children: "Value too large to display" }),
64069
64267
  /* @__PURE__ */ jsxs(Text, { color: "fg.muted", fontSize: "sm", children: [
@@ -64078,9 +64276,14 @@ const DatasetPreview = memo(function DatasetPreview2({
64078
64276
  }
64079
64277
  if (valueQuery.isLoading || !valueQuery.data) return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "loading", title: "Loading..." });
64080
64278
  if (valueQuery.error) return /* @__PURE__ */ jsx$1(StatusDisplay, { variant: "error", title: "Decode failed", message: valueQuery.error.message });
64279
+ const count = type === void 0 || decoded === void 0 ? null : type.type === "Array" ? decoded.length : type.type === "Set" || type.type === "Dict" ? decoded.size : null;
64280
+ const countText = count === null ? "" : `${count.toLocaleString()} ${(type == null ? void 0 : type.type) === "Dict" ? "entries" : "items"} · `;
64081
64281
  return /* @__PURE__ */ jsxs(Flex, { direction: "column", height: "100%", overflow: "hidden", children: [
64082
64282
  /* @__PURE__ */ jsxs(Flex, { px: 4, py: 2, justify: "flex-end", flexShrink: 0, borderBottom: "1px solid", borderColor: "border.subtle", children: [
64083
- /* @__PURE__ */ jsx$1(Text, { fontSize: "xs", color: "fg.muted", mr: 2, alignSelf: "center", children: formatSize(sizeBytes) }),
64283
+ /* @__PURE__ */ jsxs(Text, { fontSize: "xs", color: "fg.muted", mr: 2, alignSelf: "center", children: [
64284
+ countText,
64285
+ formatSize(sizeBytes)
64286
+ ] }),
64084
64287
  /* @__PURE__ */ jsx$1(DownloadButton, { onClick: download })
64085
64288
  ] }),
64086
64289
  /* @__PURE__ */ jsx$1(Box, { flex: 1, minHeight: 0, overflow: "hidden", children: treeValue !== null && /* @__PURE__ */ jsx$1(EastChakraValueTree, { value: treeValue, storageKey: path2 ?? "value" }) })
@@ -65155,6 +65358,7 @@ export {
65155
65358
  useDatasetListRecursive,
65156
65359
  useDatasetListRecursivePaths,
65157
65360
  useDatasetListWithStatus,
65361
+ useDatasetPage,
65158
65362
  useDatasetSet,
65159
65363
  useDatasetStatus,
65160
65364
  useDatasetValue,