@flanksource/clicky-ui 0.2.4 → 0.2.6
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/components/Combobox.cjs +291 -0
- package/dist/components/Combobox.cjs.map +1 -0
- package/dist/components/Combobox.d.ts +59 -0
- package/dist/components/Combobox.d.ts.map +1 -0
- package/dist/components/Combobox.js +291 -0
- package/dist/components/Combobox.js.map +1 -0
- package/dist/components/FilterBar.cjs +153 -124
- package/dist/components/FilterBar.cjs.map +1 -1
- package/dist/components/FilterBar.d.ts +25 -1
- package/dist/components/FilterBar.d.ts.map +1 -1
- package/dist/components/FilterBar.js +153 -124
- package/dist/components/FilterBar.js.map +1 -1
- package/dist/components/FormatOptionsDropdown.cjs +62 -0
- package/dist/components/FormatOptionsDropdown.cjs.map +1 -0
- package/dist/components/FormatOptionsDropdown.d.ts +37 -0
- package/dist/components/FormatOptionsDropdown.d.ts.map +1 -0
- package/dist/components/FormatOptionsDropdown.js +62 -0
- package/dist/components/FormatOptionsDropdown.js.map +1 -0
- package/dist/components/SplitButton.cjs +87 -0
- package/dist/components/SplitButton.cjs.map +1 -0
- package/dist/components/SplitButton.d.ts +32 -0
- package/dist/components/SplitButton.d.ts.map +1 -0
- package/dist/components/SplitButton.js +87 -0
- package/dist/components/SplitButton.js.map +1 -0
- package/dist/components/icon-menu-picker.cjs +1 -1
- package/dist/components/icon-menu-picker.cjs.map +1 -1
- package/dist/components/icon-menu-picker.js +1 -1
- package/dist/components/icon-menu-picker.js.map +1 -1
- package/dist/components.cjs +7 -0
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +7 -0
- package/dist/components.js.map +1 -1
- package/dist/data/Badge.d.ts +1 -1
- package/dist/data/ProgressBar.cjs +4 -2
- package/dist/data/ProgressBar.cjs.map +1 -1
- package/dist/data/ProgressBar.d.ts +1 -1
- package/dist/data/ProgressBar.d.ts.map +1 -1
- package/dist/data/ProgressBar.js +4 -2
- package/dist/data/ProgressBar.js.map +1 -1
- package/dist/data/StatusBreakdown.cjs +80 -0
- package/dist/data/StatusBreakdown.cjs.map +1 -0
- package/dist/data/StatusBreakdown.d.ts +36 -0
- package/dist/data/StatusBreakdown.d.ts.map +1 -0
- package/dist/data/StatusBreakdown.js +80 -0
- package/dist/data/StatusBreakdown.js.map +1 -0
- package/dist/data/TaskManager.cjs +162 -0
- package/dist/data/TaskManager.cjs.map +1 -0
- package/dist/data/TaskManager.d.ts +21 -0
- package/dist/data/TaskManager.d.ts.map +1 -0
- package/dist/data/TaskManager.js +162 -0
- package/dist/data/TaskManager.js.map +1 -0
- package/dist/data/TaskProgress.cjs +125 -0
- package/dist/data/TaskProgress.cjs.map +1 -0
- package/dist/data/TaskProgress.d.ts +20 -0
- package/dist/data/TaskProgress.d.ts.map +1 -0
- package/dist/data/TaskProgress.js +125 -0
- package/dist/data/TaskProgress.js.map +1 -0
- package/dist/data/TaskSnapshot.cjs +23 -0
- package/dist/data/TaskSnapshot.cjs.map +1 -0
- package/dist/data/TaskSnapshot.d.ts +49 -0
- package/dist/data/TaskSnapshot.d.ts.map +1 -0
- package/dist/data/TaskSnapshot.js +23 -0
- package/dist/data/TaskSnapshot.js.map +1 -0
- package/dist/data/TimeseriesGauge.cjs +144 -0
- package/dist/data/TimeseriesGauge.cjs.map +1 -0
- package/dist/data/TimeseriesGauge.d.ts +56 -0
- package/dist/data/TimeseriesGauge.d.ts.map +1 -0
- package/dist/data/TimeseriesGauge.js +144 -0
- package/dist/data/TimeseriesGauge.js.map +1 -0
- package/dist/data/TimeseriesPanel.axes.cjs +39 -0
- package/dist/data/TimeseriesPanel.axes.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.axes.d.ts +28 -0
- package/dist/data/TimeseriesPanel.axes.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.axes.js +39 -0
- package/dist/data/TimeseriesPanel.axes.js.map +1 -0
- package/dist/data/TimeseriesPanel.cjs +324 -0
- package/dist/data/TimeseriesPanel.cjs.map +1 -0
- package/dist/data/TimeseriesPanel.d.ts +141 -0
- package/dist/data/TimeseriesPanel.d.ts.map +1 -0
- package/dist/data/TimeseriesPanel.js +324 -0
- package/dist/data/TimeseriesPanel.js.map +1 -0
- package/dist/data/Tree.cjs.map +1 -1
- package/dist/data/Tree.d.ts.map +1 -1
- package/dist/data/Tree.js.map +1 -1
- package/dist/data/TreeNode.cjs +44 -7
- package/dist/data/TreeNode.cjs.map +1 -1
- package/dist/data/TreeNode.d.ts +19 -1
- package/dist/data/TreeNode.d.ts.map +1 -1
- package/dist/data/TreeNode.js +44 -7
- package/dist/data/TreeNode.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs +66 -44
- package/dist/data/diagnostics/DiagnosticsDetailPanel.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts +21 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.d.ts.map +1 -1
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js +65 -43
- package/dist/data/diagnostics/DiagnosticsDetailPanel.js.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.cjs.map +1 -1
- package/dist/data/diagnostics/DiagnosticsTree.js +2 -1
- package/dist/data/diagnostics/DiagnosticsTree.js.map +1 -1
- package/dist/data/diagnostics/FrameSourceWindow.cjs +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.cjs.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts +15 -0
- package/dist/data/diagnostics/FrameSourceWindow.d.ts.map +1 -0
- package/dist/data/diagnostics/FrameSourceWindow.js +37 -0
- package/dist/data/diagnostics/FrameSourceWindow.js.map +1 -0
- package/dist/data/diagnostics/JvmStackTrace.cjs +25 -12
- package/dist/data/diagnostics/JvmStackTrace.cjs.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts +8 -1
- package/dist/data/diagnostics/JvmStackTrace.d.ts.map +1 -1
- package/dist/data/diagnostics/JvmStackTrace.js +26 -13
- package/dist/data/diagnostics/JvmStackTrace.js.map +1 -1
- package/dist/data/diagnostics/ThreadCard.cjs +9 -2
- package/dist/data/diagnostics/ThreadCard.cjs.map +1 -1
- package/dist/data/diagnostics/ThreadCard.d.ts +3 -1
- package/dist/data/diagnostics/ThreadCard.d.ts.map +1 -1
- package/dist/data/diagnostics/ThreadCard.js +9 -2
- package/dist/data/diagnostics/ThreadCard.js.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
- package/dist/data/diagnostics/jvm-stacktrace.js +10 -1
- package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
- package/dist/data/diagnostics/utils.cjs +0 -12
- package/dist/data/diagnostics/utils.cjs.map +1 -1
- package/dist/data/diagnostics/utils.d.ts +1 -1
- package/dist/data/diagnostics/utils.d.ts.map +1 -1
- package/dist/data/diagnostics/utils.js +0 -12
- package/dist/data/diagnostics/utils.js.map +1 -1
- package/dist/data/task-status.cjs +108 -0
- package/dist/data/task-status.cjs.map +1 -0
- package/dist/data/task-status.d.ts +21 -0
- package/dist/data/task-status.d.ts.map +1 -0
- package/dist/data/task-status.js +108 -0
- package/dist/data/task-status.js.map +1 -0
- package/dist/data.cjs +27 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +9 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +27 -0
- package/dist/data.js.map +1 -1
- package/dist/hooks/use-task-run.cjs +137 -0
- package/dist/hooks/use-task-run.cjs.map +1 -0
- package/dist/hooks/use-task-run.d.ts +35 -0
- package/dist/hooks/use-task-run.d.ts.map +1 -0
- package/dist/hooks/use-task-run.js +137 -0
- package/dist/hooks/use-task-run.js.map +1 -0
- package/dist/hooks.cjs +3 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +3 -0
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +43 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/color.cjs +33 -0
- package/dist/lib/color.cjs.map +1 -0
- package/dist/lib/color.d.ts +10 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +33 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/lib/format.cjs +77 -0
- package/dist/lib/format.cjs.map +1 -0
- package/dist/lib/format.d.ts +30 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +77 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/overlay/DropdownMenu.cjs +18 -2
- package/dist/overlay/DropdownMenu.cjs.map +1 -1
- package/dist/overlay/DropdownMenu.d.ts +3 -1
- package/dist/overlay/DropdownMenu.d.ts.map +1 -1
- package/dist/overlay/DropdownMenu.js +18 -2
- package/dist/overlay/DropdownMenu.js.map +1 -1
- package/dist/rpc/types.cjs.map +1 -1
- package/dist/rpc/types.d.ts +4 -0
- package/dist/rpc/types.d.ts.map +1 -1
- package/dist/rpc/types.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils.cjs +6 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +6 -0
- package/dist/utils.js.map +1 -1
- package/package.json +4 -1
package/dist/data/TreeNode.cjs
CHANGED
|
@@ -20,10 +20,18 @@ function TreeNode({
|
|
|
20
20
|
rowClass,
|
|
21
21
|
indentPx = 16,
|
|
22
22
|
basePaddingPx = 8,
|
|
23
|
-
isSecondary
|
|
23
|
+
isSecondary,
|
|
24
|
+
hasMoreChildren,
|
|
25
|
+
loadChildren
|
|
24
26
|
}) {
|
|
25
|
-
const
|
|
27
|
+
const [loadedChildren, setLoadedChildren] = react.useState(null);
|
|
28
|
+
const [loading, setLoading] = react.useState(false);
|
|
29
|
+
const [error, setError] = react.useState(null);
|
|
30
|
+
const syncChildren = getChildren(node);
|
|
31
|
+
const children = loadedChildren ?? syncChildren;
|
|
26
32
|
const hasChildren = ((children == null ? void 0 : children.length) ?? 0) > 0;
|
|
33
|
+
const lazyUnloaded = ((hasMoreChildren == null ? void 0 : hasMoreChildren(node)) ?? false) && loadedChildren === null;
|
|
34
|
+
const expandable = hasChildren || lazyUnloaded;
|
|
27
35
|
const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;
|
|
28
36
|
const [open, setOpen] = react.useState(initialOpen);
|
|
29
37
|
const prevExpandAll = react.useRef(expandAll);
|
|
@@ -40,8 +48,33 @@ function TreeNode({
|
|
|
40
48
|
}
|
|
41
49
|
prevExpandAll.current = expandAll;
|
|
42
50
|
}, [expandAll, skipExpandAll]);
|
|
51
|
+
const loadRequested = react.useRef(false);
|
|
52
|
+
const nodeKey = getKey(node);
|
|
53
|
+
const loadKeyRef = react.useRef(nodeKey);
|
|
54
|
+
function loadLazyChildren() {
|
|
55
|
+
if (!loadChildren || loadRequested.current) return;
|
|
56
|
+
loadRequested.current = true;
|
|
57
|
+
loadKeyRef.current = nodeKey;
|
|
58
|
+
setLoading(true);
|
|
59
|
+
setError(null);
|
|
60
|
+
loadChildren(node).then((kids) => {
|
|
61
|
+
if (loadKeyRef.current !== nodeKey) return;
|
|
62
|
+
setLoadedChildren(kids);
|
|
63
|
+
setLoading(false);
|
|
64
|
+
}).catch((err) => {
|
|
65
|
+
if (loadKeyRef.current !== nodeKey) return;
|
|
66
|
+
loadRequested.current = false;
|
|
67
|
+
setError(err);
|
|
68
|
+
setLoading(false);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
43
71
|
function toggle() {
|
|
44
|
-
if (
|
|
72
|
+
if (!expandable) return;
|
|
73
|
+
setOpen((o) => {
|
|
74
|
+
const next = !o;
|
|
75
|
+
if (next && lazyUnloaded) loadLazyChildren();
|
|
76
|
+
return next;
|
|
77
|
+
});
|
|
45
78
|
}
|
|
46
79
|
const defaultRowBg = isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent";
|
|
47
80
|
const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;
|
|
@@ -49,7 +82,7 @@ function TreeNode({
|
|
|
49
82
|
"div",
|
|
50
83
|
{
|
|
51
84
|
role: "treeitem",
|
|
52
|
-
"aria-expanded":
|
|
85
|
+
"aria-expanded": expandable ? isOpen : void 0,
|
|
53
86
|
"aria-selected": isSelected,
|
|
54
87
|
children: [
|
|
55
88
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -63,7 +96,7 @@ function TreeNode({
|
|
|
63
96
|
toggle();
|
|
64
97
|
},
|
|
65
98
|
children: [
|
|
66
|
-
|
|
99
|
+
expandable ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
67
100
|
Icon.Icon,
|
|
68
101
|
{
|
|
69
102
|
icon: isOpen ? UiChevronDown.UiChevronDown : UiChevronRight.UiChevronRight,
|
|
@@ -76,7 +109,9 @@ function TreeNode({
|
|
|
76
109
|
open: isOpen,
|
|
77
110
|
selected: isSelected,
|
|
78
111
|
hasChildren,
|
|
79
|
-
toggle
|
|
112
|
+
toggle,
|
|
113
|
+
loading,
|
|
114
|
+
error
|
|
80
115
|
})
|
|
81
116
|
]
|
|
82
117
|
}
|
|
@@ -97,7 +132,9 @@ function TreeNode({
|
|
|
97
132
|
...defaultOpen ? { defaultOpen } : {},
|
|
98
133
|
...onSelect ? { onSelect } : {},
|
|
99
134
|
...rowClass ? { rowClass } : {},
|
|
100
|
-
...isSecondary ? { isSecondary } : {}
|
|
135
|
+
...isSecondary ? { isSecondary } : {},
|
|
136
|
+
...hasMoreChildren ? { hasMoreChildren } : {},
|
|
137
|
+
...loadChildren ? { loadChildren } : {}
|
|
101
138
|
},
|
|
102
139
|
getKey(child)
|
|
103
140
|
)) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeNode.cjs","sources":["../../src/data/TreeNode.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"./Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type TreeRowContext<T> = {\n node: T;\n depth: number;\n open: boolean;\n selected: boolean;\n hasChildren: boolean;\n toggle: () => void;\n};\n\nexport type TreeNodeProps<T> = {\n node: T;\n depth?: number;\n expandAll?: boolean | null;\n forcedOpenKeys?: Set<string | number> | null;\n selected?: T | null;\n defaultOpen?: (node: T, depth: number) => boolean;\n getChildren: (node: T) => T[] | undefined;\n getKey: (node: T) => string | number;\n onSelect?: (node: T) => void;\n renderRow: (ctx: TreeRowContext<T>) => ReactNode;\n rowClass?: (node: T, selected: boolean) => string;\n indentPx?: number;\n basePaddingPx?: number;\n /**\n * Marks a node as a **secondary child** of its parent. Secondary\n * children keep the full tree structure (they render, they can be\n * opened manually) but opt out of the bulk behaviours a caller\n * typically wants when they click the toolbar:\n *\n * - Tree-wide filter: their text is skipped during match detection\n * so typing in the search box never surfaces a row because one\n * of its secondary children happened to match.\n * - Expand all: secondary nodes stay at their default-open state\n * instead of being forced open, preventing 28k field rows from\n * flooding the viewport when an operator hits the button on a\n * schema with hundreds of tables.\n *\n * Default: every node is primary (returns false).\n */\n isSecondary?: (node: T) => boolean;\n};\n\nexport function TreeNode<T>({\n node,\n depth = 0,\n expandAll = null,\n forcedOpenKeys = null,\n selected = null,\n defaultOpen,\n getChildren,\n getKey,\n onSelect,\n renderRow,\n rowClass,\n indentPx = 16,\n basePaddingPx = 8,\n isSecondary,\n}: TreeNodeProps<T>) {\n const children = getChildren(node);\n const hasChildren = (children?.length ?? 0) > 0;\n const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;\n const [open, setOpen] = useState(initialOpen);\n const prevExpandAll = useRef(expandAll);\n const isSelected = selected === node;\n const key = getKey(node);\n const isForcedOpen = forcedOpenKeys?.has(key) ?? false;\n const isOpen = isForcedOpen || open;\n // A node opts out of bulk expand-all in two distinct cases:\n //\n // 1. The node itself is secondary (its edge from the parent is\n // secondary) — rare at the root, but the rule is symmetric.\n // 2. All of the node's children are secondary — the common case.\n // This is what keeps an \"Expand all\" on a schema tree from\n // flooding the viewport with every table's field rows: the\n // table opens to nothing structural worth seeing in bulk, so\n // we leave it closed until the operator clicks it manually.\n //\n // In both cases the manual toggle still works, and filter matches\n // still force the row open via forcedOpenKeys, so users never\n // lose access — they just don't get auto-flooded.\n const secondaryNode = isSecondary?.(node) ?? false;\n const childrenAllSecondary =\n hasChildren && isSecondary != null && children!.every((c) => isSecondary(c));\n const skipExpandAll = secondaryNode || childrenAllSecondary;\n\n useEffect(() => {\n if (expandAll !== null && expandAll !== prevExpandAll.current && !skipExpandAll) {\n setOpen(expandAll);\n }\n prevExpandAll.current = expandAll;\n }, [expandAll, skipExpandAll]);\n\n function toggle() {\n if (hasChildren) setOpen((o) => !o);\n }\n\n const defaultRowBg = isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\";\n const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;\n\n return (\n <div\n role=\"treeitem\"\n aria-expanded={hasChildren ? isOpen : undefined}\n aria-selected={isSelected}\n >\n <div\n className={cn(\"flex items-center gap-1.5 py-1 px-2 cursor-pointer text-sm\", rowClassName)}\n style={{ paddingLeft: `${depth * indentPx + basePaddingPx}px` }}\n onClick={(e) => {\n e.stopPropagation();\n onSelect?.(node);\n toggle();\n }}\n >\n {hasChildren ? (\n <Icon\n icon={isOpen ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs w-3\"\n />\n ) : (\n <span className=\"w-3 shrink-0\" aria-hidden />\n )}\n {renderRow({\n node,\n depth,\n open: isOpen,\n selected: isSelected,\n hasChildren,\n toggle,\n })}\n </div>\n {isOpen && hasChildren && (\n <div role=\"group\">\n {children!.map((child) => (\n <TreeNode\n key={getKey(child)}\n node={child}\n depth={depth + 1}\n expandAll={expandAll}\n forcedOpenKeys={forcedOpenKeys}\n selected={selected}\n getChildren={getChildren}\n getKey={getKey}\n renderRow={renderRow}\n indentPx={indentPx}\n basePaddingPx={basePaddingPx}\n {...(defaultOpen ? { defaultOpen } : {})}\n {...(onSelect ? { onSelect } : {})}\n {...(rowClass ? { rowClass } : {})}\n {...(isSecondary ? { isSecondary } : {})}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","jsxs","cn","jsx","Icon","UiChevronDown","UiChevronRight"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TreeNode.cjs","sources":["../../src/data/TreeNode.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"./Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type TreeRowContext<T> = {\n node: T;\n depth: number;\n open: boolean;\n selected: boolean;\n hasChildren: boolean;\n toggle: () => void;\n /** True while this node's lazy children are being fetched via loadChildren. */\n loading: boolean;\n /** The rejection value from loadChildren, or null when there was no error. */\n error: unknown;\n};\n\nexport type TreeNodeProps<T> = {\n node: T;\n depth?: number;\n expandAll?: boolean | null;\n forcedOpenKeys?: Set<string | number> | null;\n selected?: T | null;\n defaultOpen?: (node: T, depth: number) => boolean;\n getChildren: (node: T) => T[] | undefined;\n getKey: (node: T) => string | number;\n onSelect?: (node: T) => void;\n renderRow: (ctx: TreeRowContext<T>) => ReactNode;\n rowClass?: (node: T, selected: boolean) => string;\n indentPx?: number;\n basePaddingPx?: number;\n /**\n * Reports that a node has children that are not yet loaded — it renders an\n * expand chevron even when `getChildren` returns nothing. On the first open\n * the node fetches them via `loadChildren`. Without this (the default), a\n * node is expandable only when `getChildren` already returns children.\n */\n hasMoreChildren?: (node: T) => boolean;\n /**\n * Lazily fetch a node's children the first time it is opened. Resolves to the\n * child nodes (which may themselves be lazy). A rejection is surfaced to\n * `renderRow` via `ctx.error` and is not retried until the node is collapsed\n * and reopened. Required for any node `hasMoreChildren` returns true for.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n /**\n * Marks a node as a **secondary child** of its parent. Secondary\n * children keep the full tree structure (they render, they can be\n * opened manually) but opt out of the bulk behaviours a caller\n * typically wants when they click the toolbar:\n *\n * - Tree-wide filter: their text is skipped during match detection\n * so typing in the search box never surfaces a row because one\n * of its secondary children happened to match.\n * - Expand all: secondary nodes stay at their default-open state\n * instead of being forced open, preventing 28k field rows from\n * flooding the viewport when an operator hits the button on a\n * schema with hundreds of tables.\n *\n * Default: every node is primary (returns false).\n */\n isSecondary?: (node: T) => boolean;\n};\n\nexport function TreeNode<T>({\n node,\n depth = 0,\n expandAll = null,\n forcedOpenKeys = null,\n selected = null,\n defaultOpen,\n getChildren,\n getKey,\n onSelect,\n renderRow,\n rowClass,\n indentPx = 16,\n basePaddingPx = 8,\n isSecondary,\n hasMoreChildren,\n loadChildren,\n}: TreeNodeProps<T>) {\n // Lazily-fetched children take precedence over the synchronous ones once a\n // load has resolved; until then `loadedChildren` is null and we fall back to\n // getChildren. A node can be \"lazy\" (hasMoreChildren) with no sync children.\n const [loadedChildren, setLoadedChildren] = useState<T[] | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const syncChildren = getChildren(node);\n const children = loadedChildren ?? syncChildren;\n const hasChildren = (children?.length ?? 0) > 0;\n const lazyUnloaded = (hasMoreChildren?.(node) ?? false) && loadedChildren === null;\n // The chevron shows when there is something to reveal: either real children\n // are present, or the node is lazy and has not been loaded yet.\n const expandable = hasChildren || lazyUnloaded;\n const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;\n const [open, setOpen] = useState(initialOpen);\n const prevExpandAll = useRef(expandAll);\n const isSelected = selected === node;\n const key = getKey(node);\n const isForcedOpen = forcedOpenKeys?.has(key) ?? false;\n const isOpen = isForcedOpen || open;\n // A node opts out of bulk expand-all in two distinct cases:\n //\n // 1. The node itself is secondary (its edge from the parent is\n // secondary) — rare at the root, but the rule is symmetric.\n // 2. All of the node's children are secondary — the common case.\n // This is what keeps an \"Expand all\" on a schema tree from\n // flooding the viewport with every table's field rows: the\n // table opens to nothing structural worth seeing in bulk, so\n // we leave it closed until the operator clicks it manually.\n //\n // In both cases the manual toggle still works, and filter matches\n // still force the row open via forcedOpenKeys, so users never\n // lose access — they just don't get auto-flooded.\n const secondaryNode = isSecondary?.(node) ?? false;\n const childrenAllSecondary =\n hasChildren && isSecondary != null && children!.every((c) => isSecondary(c));\n const skipExpandAll = secondaryNode || childrenAllSecondary;\n\n useEffect(() => {\n if (expandAll !== null && expandAll !== prevExpandAll.current && !skipExpandAll) {\n setOpen(expandAll);\n }\n prevExpandAll.current = expandAll;\n }, [expandAll, skipExpandAll]);\n\n // Guards a lazy fetch so a node loads its children at most once per mount and\n // a resolution arriving after the node was reused (key changed) is ignored.\n const loadRequested = useRef(false);\n const nodeKey = getKey(node);\n const loadKeyRef = useRef(nodeKey);\n\n function loadLazyChildren() {\n if (!loadChildren || loadRequested.current) return;\n loadRequested.current = true;\n loadKeyRef.current = nodeKey;\n setLoading(true);\n setError(null);\n loadChildren(node)\n .then((kids) => {\n if (loadKeyRef.current !== nodeKey) return;\n setLoadedChildren(kids);\n setLoading(false);\n })\n .catch((err) => {\n if (loadKeyRef.current !== nodeKey) return;\n // Surfaced to renderRow via ctx.error; allow a retry on re-toggle.\n loadRequested.current = false;\n setError(err);\n setLoading(false);\n });\n }\n\n function toggle() {\n if (!expandable) return;\n setOpen((o) => {\n const next = !o;\n // Only fetch when transitioning into the open state, so collapsing a\n // not-yet-loaded (e.g. previously-failed) lazy node never triggers a load.\n if (next && lazyUnloaded) loadLazyChildren();\n return next;\n });\n }\n\n const defaultRowBg = isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\";\n const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;\n\n return (\n <div\n role=\"treeitem\"\n aria-expanded={expandable ? isOpen : undefined}\n aria-selected={isSelected}\n >\n <div\n className={cn(\"flex items-center gap-1.5 py-1 px-2 cursor-pointer text-sm\", rowClassName)}\n style={{ paddingLeft: `${depth * indentPx + basePaddingPx}px` }}\n onClick={(e) => {\n e.stopPropagation();\n onSelect?.(node);\n toggle();\n }}\n >\n {expandable ? (\n <Icon\n icon={isOpen ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs w-3\"\n />\n ) : (\n <span className=\"w-3 shrink-0\" aria-hidden />\n )}\n {renderRow({\n node,\n depth,\n open: isOpen,\n selected: isSelected,\n hasChildren,\n toggle,\n loading,\n error,\n })}\n </div>\n {isOpen && hasChildren && (\n <div role=\"group\">\n {children!.map((child) => (\n <TreeNode\n key={getKey(child)}\n node={child}\n depth={depth + 1}\n expandAll={expandAll}\n forcedOpenKeys={forcedOpenKeys}\n selected={selected}\n getChildren={getChildren}\n getKey={getKey}\n renderRow={renderRow}\n indentPx={indentPx}\n basePaddingPx={basePaddingPx}\n {...(defaultOpen ? { defaultOpen } : {})}\n {...(onSelect ? { onSelect } : {})}\n {...(rowClass ? { rowClass } : {})}\n {...(isSecondary ? { isSecondary } : {})}\n {...(hasMoreChildren ? { hasMoreChildren } : {})}\n {...(loadChildren ? { loadChildren } : {})}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":["useState","useRef","useEffect","jsxs","cn","jsx","Icon","UiChevronDown","UiChevronRight"],"mappings":"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"}
|
package/dist/data/TreeNode.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export type TreeRowContext<T> = {
|
|
|
6
6
|
selected: boolean;
|
|
7
7
|
hasChildren: boolean;
|
|
8
8
|
toggle: () => void;
|
|
9
|
+
/** True while this node's lazy children are being fetched via loadChildren. */
|
|
10
|
+
loading: boolean;
|
|
11
|
+
/** The rejection value from loadChildren, or null when there was no error. */
|
|
12
|
+
error: unknown;
|
|
9
13
|
};
|
|
10
14
|
export type TreeNodeProps<T> = {
|
|
11
15
|
node: T;
|
|
@@ -21,6 +25,20 @@ export type TreeNodeProps<T> = {
|
|
|
21
25
|
rowClass?: (node: T, selected: boolean) => string;
|
|
22
26
|
indentPx?: number;
|
|
23
27
|
basePaddingPx?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Reports that a node has children that are not yet loaded — it renders an
|
|
30
|
+
* expand chevron even when `getChildren` returns nothing. On the first open
|
|
31
|
+
* the node fetches them via `loadChildren`. Without this (the default), a
|
|
32
|
+
* node is expandable only when `getChildren` already returns children.
|
|
33
|
+
*/
|
|
34
|
+
hasMoreChildren?: (node: T) => boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Lazily fetch a node's children the first time it is opened. Resolves to the
|
|
37
|
+
* child nodes (which may themselves be lazy). A rejection is surfaced to
|
|
38
|
+
* `renderRow` via `ctx.error` and is not retried until the node is collapsed
|
|
39
|
+
* and reopened. Required for any node `hasMoreChildren` returns true for.
|
|
40
|
+
*/
|
|
41
|
+
loadChildren?: (node: T) => Promise<T[]>;
|
|
24
42
|
/**
|
|
25
43
|
* Marks a node as a **secondary child** of its parent. Secondary
|
|
26
44
|
* children keep the full tree structure (they render, they can be
|
|
@@ -39,5 +57,5 @@ export type TreeNodeProps<T> = {
|
|
|
39
57
|
*/
|
|
40
58
|
isSecondary?: (node: T) => boolean;
|
|
41
59
|
};
|
|
42
|
-
export declare function TreeNode<T>({ node, depth, expandAll, forcedOpenKeys, selected, defaultOpen, getChildren, getKey, onSelect, renderRow, rowClass, indentPx, basePaddingPx, isSecondary, }: TreeNodeProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
export declare function TreeNode<T>({ node, depth, expandAll, forcedOpenKeys, selected, defaultOpen, getChildren, getKey, onSelect, renderRow, rowClass, indentPx, basePaddingPx, isSecondary, hasMoreChildren, loadChildren, }: TreeNodeProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
43
61
|
//# sourceMappingURL=TreeNode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeNode.d.ts","sourceRoot":"","sources":["../../src/data/TreeNode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKpE,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"TreeNode.d.ts","sourceRoot":"","sources":["../../src/data/TreeNode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKpE,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,+EAA+E;IAC/E,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAClD,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,SAAS,CAAC;IAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,SAAS,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;IACjD,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACzC;;;;;;;;;;;;;;;OAeG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CACpC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAC1B,IAAI,EACJ,KAAS,EACT,SAAgB,EAChB,cAAqB,EACrB,QAAe,EACf,WAAW,EACX,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAa,EACb,aAAiB,EACjB,WAAW,EACX,eAAe,EACf,YAAY,GACb,EAAE,aAAa,CAAC,CAAC,CAAC,2CAoJlB"}
|
package/dist/data/TreeNode.js
CHANGED
|
@@ -18,10 +18,18 @@ function TreeNode({
|
|
|
18
18
|
rowClass,
|
|
19
19
|
indentPx = 16,
|
|
20
20
|
basePaddingPx = 8,
|
|
21
|
-
isSecondary
|
|
21
|
+
isSecondary,
|
|
22
|
+
hasMoreChildren,
|
|
23
|
+
loadChildren
|
|
22
24
|
}) {
|
|
23
|
-
const
|
|
25
|
+
const [loadedChildren, setLoadedChildren] = useState(null);
|
|
26
|
+
const [loading, setLoading] = useState(false);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const syncChildren = getChildren(node);
|
|
29
|
+
const children = loadedChildren ?? syncChildren;
|
|
24
30
|
const hasChildren = ((children == null ? void 0 : children.length) ?? 0) > 0;
|
|
31
|
+
const lazyUnloaded = ((hasMoreChildren == null ? void 0 : hasMoreChildren(node)) ?? false) && loadedChildren === null;
|
|
32
|
+
const expandable = hasChildren || lazyUnloaded;
|
|
25
33
|
const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;
|
|
26
34
|
const [open, setOpen] = useState(initialOpen);
|
|
27
35
|
const prevExpandAll = useRef(expandAll);
|
|
@@ -38,8 +46,33 @@ function TreeNode({
|
|
|
38
46
|
}
|
|
39
47
|
prevExpandAll.current = expandAll;
|
|
40
48
|
}, [expandAll, skipExpandAll]);
|
|
49
|
+
const loadRequested = useRef(false);
|
|
50
|
+
const nodeKey = getKey(node);
|
|
51
|
+
const loadKeyRef = useRef(nodeKey);
|
|
52
|
+
function loadLazyChildren() {
|
|
53
|
+
if (!loadChildren || loadRequested.current) return;
|
|
54
|
+
loadRequested.current = true;
|
|
55
|
+
loadKeyRef.current = nodeKey;
|
|
56
|
+
setLoading(true);
|
|
57
|
+
setError(null);
|
|
58
|
+
loadChildren(node).then((kids) => {
|
|
59
|
+
if (loadKeyRef.current !== nodeKey) return;
|
|
60
|
+
setLoadedChildren(kids);
|
|
61
|
+
setLoading(false);
|
|
62
|
+
}).catch((err) => {
|
|
63
|
+
if (loadKeyRef.current !== nodeKey) return;
|
|
64
|
+
loadRequested.current = false;
|
|
65
|
+
setError(err);
|
|
66
|
+
setLoading(false);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
41
69
|
function toggle() {
|
|
42
|
-
if (
|
|
70
|
+
if (!expandable) return;
|
|
71
|
+
setOpen((o) => {
|
|
72
|
+
const next = !o;
|
|
73
|
+
if (next && lazyUnloaded) loadLazyChildren();
|
|
74
|
+
return next;
|
|
75
|
+
});
|
|
43
76
|
}
|
|
44
77
|
const defaultRowBg = isSelected ? "bg-primary/10 border-l-2 border-primary" : "hover:bg-accent";
|
|
45
78
|
const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;
|
|
@@ -47,7 +80,7 @@ function TreeNode({
|
|
|
47
80
|
"div",
|
|
48
81
|
{
|
|
49
82
|
role: "treeitem",
|
|
50
|
-
"aria-expanded":
|
|
83
|
+
"aria-expanded": expandable ? isOpen : void 0,
|
|
51
84
|
"aria-selected": isSelected,
|
|
52
85
|
children: [
|
|
53
86
|
/* @__PURE__ */ jsxs(
|
|
@@ -61,7 +94,7 @@ function TreeNode({
|
|
|
61
94
|
toggle();
|
|
62
95
|
},
|
|
63
96
|
children: [
|
|
64
|
-
|
|
97
|
+
expandable ? /* @__PURE__ */ jsx(
|
|
65
98
|
Icon,
|
|
66
99
|
{
|
|
67
100
|
icon: isOpen ? UiChevronDown : UiChevronRight,
|
|
@@ -74,7 +107,9 @@ function TreeNode({
|
|
|
74
107
|
open: isOpen,
|
|
75
108
|
selected: isSelected,
|
|
76
109
|
hasChildren,
|
|
77
|
-
toggle
|
|
110
|
+
toggle,
|
|
111
|
+
loading,
|
|
112
|
+
error
|
|
78
113
|
})
|
|
79
114
|
]
|
|
80
115
|
}
|
|
@@ -95,7 +130,9 @@ function TreeNode({
|
|
|
95
130
|
...defaultOpen ? { defaultOpen } : {},
|
|
96
131
|
...onSelect ? { onSelect } : {},
|
|
97
132
|
...rowClass ? { rowClass } : {},
|
|
98
|
-
...isSecondary ? { isSecondary } : {}
|
|
133
|
+
...isSecondary ? { isSecondary } : {},
|
|
134
|
+
...hasMoreChildren ? { hasMoreChildren } : {},
|
|
135
|
+
...loadChildren ? { loadChildren } : {}
|
|
99
136
|
},
|
|
100
137
|
getKey(child)
|
|
101
138
|
)) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeNode.js","sources":["../../src/data/TreeNode.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"./Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type TreeRowContext<T> = {\n node: T;\n depth: number;\n open: boolean;\n selected: boolean;\n hasChildren: boolean;\n toggle: () => void;\n};\n\nexport type TreeNodeProps<T> = {\n node: T;\n depth?: number;\n expandAll?: boolean | null;\n forcedOpenKeys?: Set<string | number> | null;\n selected?: T | null;\n defaultOpen?: (node: T, depth: number) => boolean;\n getChildren: (node: T) => T[] | undefined;\n getKey: (node: T) => string | number;\n onSelect?: (node: T) => void;\n renderRow: (ctx: TreeRowContext<T>) => ReactNode;\n rowClass?: (node: T, selected: boolean) => string;\n indentPx?: number;\n basePaddingPx?: number;\n /**\n * Marks a node as a **secondary child** of its parent. Secondary\n * children keep the full tree structure (they render, they can be\n * opened manually) but opt out of the bulk behaviours a caller\n * typically wants when they click the toolbar:\n *\n * - Tree-wide filter: their text is skipped during match detection\n * so typing in the search box never surfaces a row because one\n * of its secondary children happened to match.\n * - Expand all: secondary nodes stay at their default-open state\n * instead of being forced open, preventing 28k field rows from\n * flooding the viewport when an operator hits the button on a\n * schema with hundreds of tables.\n *\n * Default: every node is primary (returns false).\n */\n isSecondary?: (node: T) => boolean;\n};\n\nexport function TreeNode<T>({\n node,\n depth = 0,\n expandAll = null,\n forcedOpenKeys = null,\n selected = null,\n defaultOpen,\n getChildren,\n getKey,\n onSelect,\n renderRow,\n rowClass,\n indentPx = 16,\n basePaddingPx = 8,\n isSecondary,\n}: TreeNodeProps<T>) {\n const children = getChildren(node);\n const hasChildren = (children?.length ?? 0) > 0;\n const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;\n const [open, setOpen] = useState(initialOpen);\n const prevExpandAll = useRef(expandAll);\n const isSelected = selected === node;\n const key = getKey(node);\n const isForcedOpen = forcedOpenKeys?.has(key) ?? false;\n const isOpen = isForcedOpen || open;\n // A node opts out of bulk expand-all in two distinct cases:\n //\n // 1. The node itself is secondary (its edge from the parent is\n // secondary) — rare at the root, but the rule is symmetric.\n // 2. All of the node's children are secondary — the common case.\n // This is what keeps an \"Expand all\" on a schema tree from\n // flooding the viewport with every table's field rows: the\n // table opens to nothing structural worth seeing in bulk, so\n // we leave it closed until the operator clicks it manually.\n //\n // In both cases the manual toggle still works, and filter matches\n // still force the row open via forcedOpenKeys, so users never\n // lose access — they just don't get auto-flooded.\n const secondaryNode = isSecondary?.(node) ?? false;\n const childrenAllSecondary =\n hasChildren && isSecondary != null && children!.every((c) => isSecondary(c));\n const skipExpandAll = secondaryNode || childrenAllSecondary;\n\n useEffect(() => {\n if (expandAll !== null && expandAll !== prevExpandAll.current && !skipExpandAll) {\n setOpen(expandAll);\n }\n prevExpandAll.current = expandAll;\n }, [expandAll, skipExpandAll]);\n\n function toggle() {\n if (hasChildren) setOpen((o) => !o);\n }\n\n const defaultRowBg = isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\";\n const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;\n\n return (\n <div\n role=\"treeitem\"\n aria-expanded={hasChildren ? isOpen : undefined}\n aria-selected={isSelected}\n >\n <div\n className={cn(\"flex items-center gap-1.5 py-1 px-2 cursor-pointer text-sm\", rowClassName)}\n style={{ paddingLeft: `${depth * indentPx + basePaddingPx}px` }}\n onClick={(e) => {\n e.stopPropagation();\n onSelect?.(node);\n toggle();\n }}\n >\n {hasChildren ? (\n <Icon\n icon={isOpen ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs w-3\"\n />\n ) : (\n <span className=\"w-3 shrink-0\" aria-hidden />\n )}\n {renderRow({\n node,\n depth,\n open: isOpen,\n selected: isSelected,\n hasChildren,\n toggle,\n })}\n </div>\n {isOpen && hasChildren && (\n <div role=\"group\">\n {children!.map((child) => (\n <TreeNode\n key={getKey(child)}\n node={child}\n depth={depth + 1}\n expandAll={expandAll}\n forcedOpenKeys={forcedOpenKeys}\n selected={selected}\n getChildren={getChildren}\n getKey={getKey}\n renderRow={renderRow}\n indentPx={indentPx}\n basePaddingPx={basePaddingPx}\n {...(defaultOpen ? { defaultOpen } : {})}\n {...(onSelect ? { onSelect } : {})}\n {...(rowClass ? { rowClass } : {})}\n {...(isSecondary ? { isSecondary } : {})}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;AA+CO,SAAS,SAAY;AAAA,EAC1B;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB;AACF,GAAqB;AACnB,QAAM,WAAW,YAAY,IAAI;AACjC,QAAM,gBAAe,qCAAU,WAAU,KAAK;AAC9C,QAAM,cAAc,cAAc,YAAY,MAAM,KAAK,IAAI,QAAQ;AACrE,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,WAAW;AAC5C,QAAM,gBAAgB,OAAO,SAAS;AACtC,QAAM,aAAa,aAAa;AAChC,QAAM,MAAM,OAAO,IAAI;AACvB,QAAM,gBAAe,iDAAgB,IAAI,SAAQ;AACjD,QAAM,SAAS,gBAAgB;AAc/B,QAAM,iBAAgB,2CAAc,UAAS;AAC7C,QAAM,uBACJ,eAAe,eAAe,QAAQ,SAAU,MAAM,CAAC,MAAM,YAAY,CAAC,CAAC;AAC7E,QAAM,gBAAgB,iBAAiB;AAEvC,YAAU,MAAM;AACd,QAAI,cAAc,QAAQ,cAAc,cAAc,WAAW,CAAC,eAAe;AAC/E,cAAQ,SAAS;AAAA,IACnB;AACA,kBAAc,UAAU;AAAA,EAC1B,GAAG,CAAC,WAAW,aAAa,CAAC;AAE7B,WAAS,SAAS;AAChB,QAAI,YAAa,SAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,EACpC;AAEA,QAAM,eAAe,aAAa,4CAA4C;AAC9E,QAAM,eAAe,WAAW,SAAS,MAAM,UAAU,IAAI;AAE7D,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,iBAAe,cAAc,SAAS;AAAA,MACtC,iBAAe;AAAA,MAEf,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW,GAAG,8DAA8D,YAAY;AAAA,YACxF,OAAO,EAAE,aAAa,GAAG,QAAQ,WAAW,aAAa,KAAA;AAAA,YACzD,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAA;AACF,mDAAW;AACX,qBAAA;AAAA,YACF;AAAA,YAEC,UAAA;AAAA,cAAA,cACC;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,MAAM,SAAS,gBAAgB;AAAA,kBAC/B,WAAU;AAAA,gBAAA;AAAA,cAAA,IAGZ,oBAAC,QAAA,EAAK,WAAU,gBAAe,eAAW,MAAC;AAAA,cAE5C,UAAU;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA,MAAM;AAAA,gBACN,UAAU;AAAA,gBACV;AAAA,gBACA;AAAA,cAAA,CACD;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAEF,UAAU,eACT,oBAAC,OAAA,EAAI,MAAK,SACP,UAAA,SAAU,IAAI,CAAC,UACd;AAAA,UAAC;AAAA,UAAA;AAAA,YAEC,MAAM;AAAA,YACN,OAAO,QAAQ;AAAA,YACf;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACC,GAAI,cAAc,EAAE,YAAA,IAAgB,CAAA;AAAA,YACpC,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,YAC9B,GAAI,WAAW,EAAE,SAAA,IAAa,CAAA;AAAA,YAC9B,GAAI,cAAc,EAAE,gBAAgB,CAAA;AAAA,UAAC;AAAA,UAdjC,OAAO,KAAK;AAAA,QAAA,CAgBpB,EAAA,CACH;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
1
|
+
{"version":3,"file":"TreeNode.js","sources":["../../src/data/TreeNode.tsx"],"sourcesContent":["import { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"./Icon\";\nimport { UiChevronDown, UiChevronRight } from \"../icons\";\n\nexport type TreeRowContext<T> = {\n node: T;\n depth: number;\n open: boolean;\n selected: boolean;\n hasChildren: boolean;\n toggle: () => void;\n /** True while this node's lazy children are being fetched via loadChildren. */\n loading: boolean;\n /** The rejection value from loadChildren, or null when there was no error. */\n error: unknown;\n};\n\nexport type TreeNodeProps<T> = {\n node: T;\n depth?: number;\n expandAll?: boolean | null;\n forcedOpenKeys?: Set<string | number> | null;\n selected?: T | null;\n defaultOpen?: (node: T, depth: number) => boolean;\n getChildren: (node: T) => T[] | undefined;\n getKey: (node: T) => string | number;\n onSelect?: (node: T) => void;\n renderRow: (ctx: TreeRowContext<T>) => ReactNode;\n rowClass?: (node: T, selected: boolean) => string;\n indentPx?: number;\n basePaddingPx?: number;\n /**\n * Reports that a node has children that are not yet loaded — it renders an\n * expand chevron even when `getChildren` returns nothing. On the first open\n * the node fetches them via `loadChildren`. Without this (the default), a\n * node is expandable only when `getChildren` already returns children.\n */\n hasMoreChildren?: (node: T) => boolean;\n /**\n * Lazily fetch a node's children the first time it is opened. Resolves to the\n * child nodes (which may themselves be lazy). A rejection is surfaced to\n * `renderRow` via `ctx.error` and is not retried until the node is collapsed\n * and reopened. Required for any node `hasMoreChildren` returns true for.\n */\n loadChildren?: (node: T) => Promise<T[]>;\n /**\n * Marks a node as a **secondary child** of its parent. Secondary\n * children keep the full tree structure (they render, they can be\n * opened manually) but opt out of the bulk behaviours a caller\n * typically wants when they click the toolbar:\n *\n * - Tree-wide filter: their text is skipped during match detection\n * so typing in the search box never surfaces a row because one\n * of its secondary children happened to match.\n * - Expand all: secondary nodes stay at their default-open state\n * instead of being forced open, preventing 28k field rows from\n * flooding the viewport when an operator hits the button on a\n * schema with hundreds of tables.\n *\n * Default: every node is primary (returns false).\n */\n isSecondary?: (node: T) => boolean;\n};\n\nexport function TreeNode<T>({\n node,\n depth = 0,\n expandAll = null,\n forcedOpenKeys = null,\n selected = null,\n defaultOpen,\n getChildren,\n getKey,\n onSelect,\n renderRow,\n rowClass,\n indentPx = 16,\n basePaddingPx = 8,\n isSecondary,\n hasMoreChildren,\n loadChildren,\n}: TreeNodeProps<T>) {\n // Lazily-fetched children take precedence over the synchronous ones once a\n // load has resolved; until then `loadedChildren` is null and we fall back to\n // getChildren. A node can be \"lazy\" (hasMoreChildren) with no sync children.\n const [loadedChildren, setLoadedChildren] = useState<T[] | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const syncChildren = getChildren(node);\n const children = loadedChildren ?? syncChildren;\n const hasChildren = (children?.length ?? 0) > 0;\n const lazyUnloaded = (hasMoreChildren?.(node) ?? false) && loadedChildren === null;\n // The chevron shows when there is something to reveal: either real children\n // are present, or the node is lazy and has not been loaded yet.\n const expandable = hasChildren || lazyUnloaded;\n const initialOpen = defaultOpen ? defaultOpen(node, depth) : depth < 1;\n const [open, setOpen] = useState(initialOpen);\n const prevExpandAll = useRef(expandAll);\n const isSelected = selected === node;\n const key = getKey(node);\n const isForcedOpen = forcedOpenKeys?.has(key) ?? false;\n const isOpen = isForcedOpen || open;\n // A node opts out of bulk expand-all in two distinct cases:\n //\n // 1. The node itself is secondary (its edge from the parent is\n // secondary) — rare at the root, but the rule is symmetric.\n // 2. All of the node's children are secondary — the common case.\n // This is what keeps an \"Expand all\" on a schema tree from\n // flooding the viewport with every table's field rows: the\n // table opens to nothing structural worth seeing in bulk, so\n // we leave it closed until the operator clicks it manually.\n //\n // In both cases the manual toggle still works, and filter matches\n // still force the row open via forcedOpenKeys, so users never\n // lose access — they just don't get auto-flooded.\n const secondaryNode = isSecondary?.(node) ?? false;\n const childrenAllSecondary =\n hasChildren && isSecondary != null && children!.every((c) => isSecondary(c));\n const skipExpandAll = secondaryNode || childrenAllSecondary;\n\n useEffect(() => {\n if (expandAll !== null && expandAll !== prevExpandAll.current && !skipExpandAll) {\n setOpen(expandAll);\n }\n prevExpandAll.current = expandAll;\n }, [expandAll, skipExpandAll]);\n\n // Guards a lazy fetch so a node loads its children at most once per mount and\n // a resolution arriving after the node was reused (key changed) is ignored.\n const loadRequested = useRef(false);\n const nodeKey = getKey(node);\n const loadKeyRef = useRef(nodeKey);\n\n function loadLazyChildren() {\n if (!loadChildren || loadRequested.current) return;\n loadRequested.current = true;\n loadKeyRef.current = nodeKey;\n setLoading(true);\n setError(null);\n loadChildren(node)\n .then((kids) => {\n if (loadKeyRef.current !== nodeKey) return;\n setLoadedChildren(kids);\n setLoading(false);\n })\n .catch((err) => {\n if (loadKeyRef.current !== nodeKey) return;\n // Surfaced to renderRow via ctx.error; allow a retry on re-toggle.\n loadRequested.current = false;\n setError(err);\n setLoading(false);\n });\n }\n\n function toggle() {\n if (!expandable) return;\n setOpen((o) => {\n const next = !o;\n // Only fetch when transitioning into the open state, so collapsing a\n // not-yet-loaded (e.g. previously-failed) lazy node never triggers a load.\n if (next && lazyUnloaded) loadLazyChildren();\n return next;\n });\n }\n\n const defaultRowBg = isSelected ? \"bg-primary/10 border-l-2 border-primary\" : \"hover:bg-accent\";\n const rowClassName = rowClass ? rowClass(node, isSelected) : defaultRowBg;\n\n return (\n <div\n role=\"treeitem\"\n aria-expanded={expandable ? isOpen : undefined}\n aria-selected={isSelected}\n >\n <div\n className={cn(\"flex items-center gap-1.5 py-1 px-2 cursor-pointer text-sm\", rowClassName)}\n style={{ paddingLeft: `${depth * indentPx + basePaddingPx}px` }}\n onClick={(e) => {\n e.stopPropagation();\n onSelect?.(node);\n toggle();\n }}\n >\n {expandable ? (\n <Icon\n icon={isOpen ? UiChevronDown : UiChevronRight}\n className=\"text-muted-foreground text-xs w-3\"\n />\n ) : (\n <span className=\"w-3 shrink-0\" aria-hidden />\n )}\n {renderRow({\n node,\n depth,\n open: isOpen,\n selected: isSelected,\n hasChildren,\n toggle,\n loading,\n error,\n })}\n </div>\n {isOpen && hasChildren && (\n <div role=\"group\">\n {children!.map((child) => (\n <TreeNode\n key={getKey(child)}\n node={child}\n depth={depth + 1}\n expandAll={expandAll}\n forcedOpenKeys={forcedOpenKeys}\n selected={selected}\n getChildren={getChildren}\n getKey={getKey}\n renderRow={renderRow}\n indentPx={indentPx}\n basePaddingPx={basePaddingPx}\n {...(defaultOpen ? { defaultOpen } : {})}\n {...(onSelect ? { onSelect } : {})}\n {...(rowClass ? { rowClass } : {})}\n {...(isSecondary ? { isSecondary } : {})}\n {...(hasMoreChildren ? { hasMoreChildren } : {})}\n {...(loadChildren ? { loadChildren } : {})}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -9,10 +9,11 @@ const GoroutineCard = require("./GoroutineCard.cjs");
|
|
|
9
9
|
const ThreadCard = require("./ThreadCard.cjs");
|
|
10
10
|
const UiServerProcess = require("../../icons/components/UiServerProcess.cjs");
|
|
11
11
|
const UiDebug = require("../../icons/components/UiDebug.cjs");
|
|
12
|
-
const UiSearch = require("../../icons/components/UiSearch.cjs");
|
|
13
12
|
const UiLoader = require("../../icons/components/UiLoader.cjs");
|
|
14
13
|
const UiDebugSmall = require("../../icons/components/UiDebugSmall.cjs");
|
|
15
14
|
const UiRefresh = require("../../icons/components/UiRefresh.cjs");
|
|
15
|
+
const format = require("../../lib/format.cjs");
|
|
16
|
+
const UiSearch = require("../../icons/components/UiSearch.cjs");
|
|
16
17
|
const STACK_MIN_LINES = 10;
|
|
17
18
|
const STACK_LINE_HEIGHT_REM = 1;
|
|
18
19
|
const STACK_MIN_HEIGHT = `${STACK_MIN_LINES * STACK_LINE_HEIGHT_REM}rem`;
|
|
@@ -33,7 +34,10 @@ function DiagnosticsDetailPanel({
|
|
|
33
34
|
process,
|
|
34
35
|
collectBusy,
|
|
35
36
|
onCollectStack,
|
|
36
|
-
runMeta
|
|
37
|
+
runMeta,
|
|
38
|
+
resolveSource,
|
|
39
|
+
headerActions,
|
|
40
|
+
singleItem
|
|
37
41
|
}) {
|
|
38
42
|
const [search, setSearch] = react.useState("");
|
|
39
43
|
const [selectedStates, setSelectedStates] = react.useState(/* @__PURE__ */ new Set());
|
|
@@ -44,13 +48,14 @@ function DiagnosticsDetailPanel({
|
|
|
44
48
|
const filtered = react.useMemo(() => {
|
|
45
49
|
const needle = search.trim().toLowerCase();
|
|
46
50
|
const items = parsed.format === "jvm" ? parsed.threads : parsed.format === "go" ? parsed.goroutines : [];
|
|
51
|
+
if (singleItem) return items;
|
|
47
52
|
return items.filter((item) => {
|
|
48
53
|
if (selectedStates.size > 0 && !selectedStates.has(item.state)) return false;
|
|
49
54
|
if (hideRuntimeOnly && item.userFrameCount === 0) return false;
|
|
50
55
|
if (needle && !item.searchText.includes(needle)) return false;
|
|
51
56
|
return true;
|
|
52
57
|
});
|
|
53
|
-
}, [parsed, search, selectedStates, hideRuntimeOnly]);
|
|
58
|
+
}, [parsed, search, selectedStates, hideRuntimeOnly, singleItem]);
|
|
54
59
|
react.useEffect(() => {
|
|
55
60
|
setSearch("");
|
|
56
61
|
setSelectedStates(/* @__PURE__ */ new Set());
|
|
@@ -63,7 +68,16 @@ function DiagnosticsDetailPanel({
|
|
|
63
68
|
] }) });
|
|
64
69
|
}
|
|
65
70
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full min-h-0 flex flex-col gap-density-3 p-density-4", children: [
|
|
66
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
+
Header,
|
|
73
|
+
{
|
|
74
|
+
process,
|
|
75
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-density-2", children: [
|
|
76
|
+
singleItem && onCollectStack && /* @__PURE__ */ jsxRuntime.jsx(CollectButton, { pid: process.pid, busy: collectBusy ?? false, onClick: onCollectStack }),
|
|
77
|
+
headerActions
|
|
78
|
+
] })
|
|
79
|
+
}
|
|
80
|
+
),
|
|
67
81
|
runMeta && /* @__PURE__ */ jsxRuntime.jsx(RunSection, { runMeta }),
|
|
68
82
|
process.command && /* @__PURE__ */ jsxRuntime.jsx(PanelSection, { title: "Command", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-foreground whitespace-pre-wrap font-mono bg-blue-50 dark:bg-blue-500/10 rounded p-2 break-all", children: process.command }) }),
|
|
69
83
|
/* @__PURE__ */ jsxRuntime.jsx(PanelSection, { title: "Metrics", children: /* @__PURE__ */ jsxRuntime.jsx(ProcessMetrics, { process }) }),
|
|
@@ -81,46 +95,51 @@ function DiagnosticsDetailPanel({
|
|
|
81
95
|
hideRuntimeOnly,
|
|
82
96
|
setHideRuntimeOnly,
|
|
83
97
|
collectBusy: collectBusy ?? false,
|
|
84
|
-
|
|
98
|
+
singleItem: singleItem ?? false,
|
|
99
|
+
...onCollectStack ? { onCollectStack } : {},
|
|
100
|
+
...resolveSource ? { resolveSource } : {}
|
|
85
101
|
}
|
|
86
102
|
) })
|
|
87
103
|
] });
|
|
88
104
|
}
|
|
89
|
-
function Header({ process }) {
|
|
90
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
91
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
92
|
-
/* @__PURE__ */ jsxRuntime.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
function Header({ process, actions }) {
|
|
106
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-density-3", children: [
|
|
107
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
108
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-density-2 flex-wrap", children: [
|
|
109
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
110
|
+
Icon.Icon,
|
|
111
|
+
{
|
|
112
|
+
icon: process.is_root ? UiServerProcess.UiServerProcess : UiDebug.UiDebug,
|
|
113
|
+
className: "text-2xl text-blue-600"
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-bold text-foreground break-words", children: utils.processLabel(process) }),
|
|
117
|
+
process.status ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
118
|
+
"span",
|
|
119
|
+
{
|
|
120
|
+
className: `inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-xs ${utils.processStateColor(
|
|
121
|
+
process.status
|
|
122
|
+
)}`,
|
|
123
|
+
children: [
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: utils.processStateIcon(process.status) }),
|
|
125
|
+
process.status
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
) : null
|
|
105
129
|
] }),
|
|
106
|
-
|
|
107
|
-
"
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
"span",
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
) : null
|
|
122
|
-
] })
|
|
123
|
-
] }) });
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-density-2 flex-wrap text-xs text-muted-foreground", children: [
|
|
131
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
132
|
+
"pid ",
|
|
133
|
+
process.pid
|
|
134
|
+
] }),
|
|
135
|
+
process.ppid ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono", children: [
|
|
136
|
+
"ppid ",
|
|
137
|
+
process.ppid
|
|
138
|
+
] }) : null
|
|
139
|
+
] })
|
|
140
|
+
] }),
|
|
141
|
+
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: actions }) : null
|
|
142
|
+
] });
|
|
124
143
|
}
|
|
125
144
|
function RunSection({ runMeta }) {
|
|
126
145
|
return /* @__PURE__ */ jsxRuntime.jsx(PanelSection, { title: "Run", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-density-2 text-sm", children: [
|
|
@@ -166,7 +185,9 @@ function StackBlock(props) {
|
|
|
166
185
|
hideRuntimeOnly,
|
|
167
186
|
setHideRuntimeOnly,
|
|
168
187
|
collectBusy,
|
|
169
|
-
onCollectStack
|
|
188
|
+
onCollectStack,
|
|
189
|
+
resolveSource,
|
|
190
|
+
singleItem
|
|
170
191
|
} = props;
|
|
171
192
|
const stack = process.stack_capture;
|
|
172
193
|
if (!(stack == null ? void 0 : stack.text)) {
|
|
@@ -206,7 +227,7 @@ function StackBlock(props) {
|
|
|
206
227
|
] });
|
|
207
228
|
}
|
|
208
229
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full min-h-0 flex flex-col", children: [
|
|
209
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-1 py-1.5 space-y-density-2", children: [
|
|
230
|
+
!singleItem && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-1 py-1.5 space-y-density-2", children: [
|
|
210
231
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-density-2 text-[11px] text-muted-foreground flex-wrap", children: [
|
|
211
232
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-density-2 flex-wrap", children: [
|
|
212
233
|
/* @__PURE__ */ jsxRuntime.jsx(StackStatusBadge, { status: stack.status }),
|
|
@@ -314,7 +335,8 @@ function StackBlock(props) {
|
|
|
314
335
|
{
|
|
315
336
|
thread: t,
|
|
316
337
|
search,
|
|
317
|
-
hideRuntimeOnly
|
|
338
|
+
hideRuntimeOnly,
|
|
339
|
+
...resolveSource ? { resolveSource } : {}
|
|
318
340
|
},
|
|
319
341
|
t.id
|
|
320
342
|
)),
|
|
@@ -393,12 +415,12 @@ function ProcessMetrics({ process }) {
|
|
|
393
415
|
MetricTile,
|
|
394
416
|
{
|
|
395
417
|
label: "RSS",
|
|
396
|
-
value: rss !== void 0 ?
|
|
418
|
+
value: rss !== void 0 ? format.formatBytes(rss) : "n/a",
|
|
397
419
|
barPct: memRatioPct,
|
|
398
420
|
tone: memoryTone(rss, vms)
|
|
399
421
|
}
|
|
400
422
|
),
|
|
401
|
-
/* @__PURE__ */ jsxRuntime.jsx(MetricTile, { label: "VMS", value: vms !== void 0 ?
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricTile, { label: "VMS", value: vms !== void 0 ? format.formatBytes(vms) : "n/a", tone: "neutral" }),
|
|
402
424
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
403
425
|
MetricTile,
|
|
404
426
|
{
|