@abelfubu/dv 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ansi-html.d.ts +42 -0
- package/dist/ansi-html.d.ts.map +1 -0
- package/dist/ansi-html.js +327 -0
- package/dist/ansi-output.d.ts +22 -0
- package/dist/ansi-output.d.ts.map +1 -0
- package/dist/ansi-output.js +154 -0
- package/dist/balance-delimiters.d.ts +25 -0
- package/dist/balance-delimiters.d.ts.map +1 -0
- package/dist/balance-delimiters.js +539 -0
- package/dist/balance-delimiters.test.d.ts +2 -0
- package/dist/balance-delimiters.test.d.ts.map +1 -0
- package/dist/balance-delimiters.test.js +1029 -0
- package/dist/cli-copy-notification.test.d.ts +2 -0
- package/dist/cli-copy-notification.test.d.ts.map +1 -0
- package/dist/cli-copy-notification.test.js +80 -0
- package/dist/cli-scroll.test.d.ts +2 -0
- package/dist/cli-scroll.test.d.ts.map +1 -0
- package/dist/cli-scroll.test.js +283 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +976 -0
- package/dist/clipboard.d.ts +16 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +128 -0
- package/dist/components/diff-view.d.ts +32 -0
- package/dist/components/diff-view.d.ts.map +1 -0
- package/dist/components/diff-view.js +123 -0
- package/dist/components/diff-view.test.d.ts +5 -0
- package/dist/components/diff-view.test.d.ts.map +1 -0
- package/dist/components/diff-view.test.js +312 -0
- package/dist/components/directory-tree-view.d.ts +33 -0
- package/dist/components/directory-tree-view.d.ts.map +1 -0
- package/dist/components/directory-tree-view.js +262 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +5 -0
- package/dist/components/toast.d.ts +21 -0
- package/dist/components/toast.d.ts.map +1 -0
- package/dist/components/toast.js +47 -0
- package/dist/diff-cursor-utils.d.ts +20 -0
- package/dist/diff-cursor-utils.d.ts.map +1 -0
- package/dist/diff-cursor-utils.js +105 -0
- package/dist/diff-cursor-utils.test.d.ts +2 -0
- package/dist/diff-cursor-utils.test.d.ts.map +1 -0
- package/dist/diff-cursor-utils.test.js +40 -0
- package/dist/diff-surface-copy.d.ts +23 -0
- package/dist/diff-surface-copy.d.ts.map +1 -0
- package/dist/diff-surface-copy.js +64 -0
- package/dist/diff-surface-copy.test.d.ts +5 -0
- package/dist/diff-surface-copy.test.d.ts.map +1 -0
- package/dist/diff-surface-copy.test.js +142 -0
- package/dist/diff-utils.d.ts +196 -0
- package/dist/diff-utils.d.ts.map +1 -0
- package/dist/diff-utils.js +682 -0
- package/dist/diff-utils.test.d.ts +2 -0
- package/dist/diff-utils.test.d.ts.map +1 -0
- package/dist/diff-utils.test.js +727 -0
- package/dist/directory-tree.d.ts +72 -0
- package/dist/directory-tree.d.ts.map +1 -0
- package/dist/directory-tree.js +161 -0
- package/dist/directory-tree.test.d.ts +2 -0
- package/dist/directory-tree.test.d.ts.map +1 -0
- package/dist/directory-tree.test.js +383 -0
- package/dist/dropdown.d.ts +26 -0
- package/dist/dropdown.d.ts.map +1 -0
- package/dist/dropdown.js +172 -0
- package/dist/dropdown.test.d.ts +2 -0
- package/dist/dropdown.test.d.ts.map +1 -0
- package/dist/dropdown.test.js +106 -0
- package/dist/filter-submodule.e2e.test.d.ts +2 -0
- package/dist/filter-submodule.e2e.test.d.ts.map +1 -0
- package/dist/filter-submodule.e2e.test.js +109 -0
- package/dist/hooks/use-copy-selection.d.ts +29 -0
- package/dist/hooks/use-copy-selection.d.ts.map +1 -0
- package/dist/hooks/use-copy-selection.js +46 -0
- package/dist/kv-codec.d.ts +16 -0
- package/dist/kv-codec.d.ts.map +1 -0
- package/dist/kv-codec.js +36 -0
- package/dist/license.d.ts +14 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +63 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +78 -0
- package/dist/monochrome.d.ts +34 -0
- package/dist/monochrome.d.ts.map +1 -0
- package/dist/monochrome.js +613 -0
- package/dist/monotone.d.ts +22 -0
- package/dist/monotone.d.ts.map +1 -0
- package/dist/monotone.js +185 -0
- package/dist/parsers-config.d.ts +19 -0
- package/dist/parsers-config.d.ts.map +1 -0
- package/dist/parsers-config.js +271 -0
- package/dist/patch-terminal-dimensions.d.ts +2 -0
- package/dist/patch-terminal-dimensions.d.ts.map +1 -0
- package/dist/patch-terminal-dimensions.js +45 -0
- package/dist/stdin-pager.test.d.ts +2 -0
- package/dist/stdin-pager.test.d.ts.map +1 -0
- package/dist/stdin-pager.test.js +497 -0
- package/dist/store.d.ts +16 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +48 -0
- package/dist/themes/github.json +247 -0
- package/dist/themes.d.ts +59 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +248 -0
- package/dist/tree-icons.d.ts +4 -0
- package/dist/tree-icons.d.ts.map +1 -0
- package/dist/tree-icons.js +18 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +13 -0
- package/dist/web-utils.d.ts +56 -0
- package/dist/web-utils.d.ts.map +1 -0
- package/dist/web-utils.js +363 -0
- package/package.json +37 -0
- package/public/jetbrains-mono-nerd.ttf +0 -0
- package/public/jetbrains-mono-nerd.woff2 +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File status based on git diff
|
|
3
|
+
*/
|
|
4
|
+
export type FileStatus = "added" | "modified" | "deleted" | "renamed";
|
|
5
|
+
/**
|
|
6
|
+
* Input file info for tree building
|
|
7
|
+
*/
|
|
8
|
+
export interface TreeFileInfo {
|
|
9
|
+
path: string;
|
|
10
|
+
status: FileStatus;
|
|
11
|
+
additions: number;
|
|
12
|
+
deletions: number;
|
|
13
|
+
/** Optional index for scroll-to functionality */
|
|
14
|
+
fileIndex?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A single node in the rendered tree output
|
|
18
|
+
*/
|
|
19
|
+
export interface TreeNode {
|
|
20
|
+
/** Display path (may be collapsed, e.g., "src/components") */
|
|
21
|
+
displayPath: string;
|
|
22
|
+
/** Full path from repo root (e.g., "src/components") */
|
|
23
|
+
fullPath: string;
|
|
24
|
+
/** Whether this is a file (true) or directory (false) */
|
|
25
|
+
isFile: boolean;
|
|
26
|
+
/** File index for scroll-to (only for files) */
|
|
27
|
+
fileIndex?: number;
|
|
28
|
+
/** File status (only for files) */
|
|
29
|
+
status?: FileStatus;
|
|
30
|
+
/** Number of added lines (only for files) */
|
|
31
|
+
additions?: number;
|
|
32
|
+
/** Number of deleted lines (only for files) */
|
|
33
|
+
deletions?: number;
|
|
34
|
+
/** Tree connector: spaces for indentation (no ASCII art) */
|
|
35
|
+
connector: string;
|
|
36
|
+
/** Prefix string for tree indentation, e.g., " " */
|
|
37
|
+
prefix: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A hierarchical tree node with children for sidebar collapse/expand
|
|
41
|
+
*/
|
|
42
|
+
export interface HierarchicalTreeNode {
|
|
43
|
+
/** Display path (may be collapsed, e.g., "src/components") */
|
|
44
|
+
displayPath: string;
|
|
45
|
+
/** Full path from repo root (e.g., "src/components") */
|
|
46
|
+
fullPath: string;
|
|
47
|
+
/** Whether this is a file (true) or directory (false) */
|
|
48
|
+
isFile: boolean;
|
|
49
|
+
/** File index for scroll-to (only for files) */
|
|
50
|
+
fileIndex?: number;
|
|
51
|
+
/** File status (only for files) */
|
|
52
|
+
status?: FileStatus;
|
|
53
|
+
/** Number of added lines (only for files) */
|
|
54
|
+
additions?: number;
|
|
55
|
+
/** Number of deleted lines (only for files) */
|
|
56
|
+
deletions?: number;
|
|
57
|
+
/** Child nodes */
|
|
58
|
+
children: HierarchicalTreeNode[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build a hierarchical tree from file paths
|
|
62
|
+
* @param files Array of file info objects
|
|
63
|
+
* @returns Array of HierarchicalTreeNode objects with children
|
|
64
|
+
*/
|
|
65
|
+
export declare function buildHierarchicalTree(files: TreeFileInfo[]): HierarchicalTreeNode[];
|
|
66
|
+
/**
|
|
67
|
+
* Build a directory tree from file paths
|
|
68
|
+
* @param files Array of file info objects
|
|
69
|
+
* @returns Array of TreeNode objects ready for rendering
|
|
70
|
+
*/
|
|
71
|
+
export declare function buildDirectoryTree(files: TreeFileInfo[]): TreeNode[];
|
|
72
|
+
//# sourceMappingURL=directory-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-tree.d.ts","sourceRoot":"","sources":["../src/directory-tree.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAErE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAA;IACnB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,MAAM,EAAE,OAAO,CAAA;IACf,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kBAAkB;IAClB,QAAQ,EAAE,oBAAoB,EAAE,CAAA;CACjC;AAYD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,oBAAoB,EAAE,CAQnF;AAoBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAQpE"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Directory tree builder for displaying file changes in a tree structure.
|
|
2
|
+
// Builds a collapsible tree from file paths with status colors and change counts.
|
|
3
|
+
// Returns structured nodes that can be rendered by DirectoryTreeView component.
|
|
4
|
+
/**
|
|
5
|
+
* Build a hierarchical tree from file paths
|
|
6
|
+
* @param files Array of file info objects
|
|
7
|
+
* @returns Array of HierarchicalTreeNode objects with children
|
|
8
|
+
*/
|
|
9
|
+
export function buildHierarchicalTree(files) {
|
|
10
|
+
if (files.length === 0) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const tree = buildInternalTree(files);
|
|
14
|
+
sortInternalTree(tree);
|
|
15
|
+
return toHierarchical(tree);
|
|
16
|
+
}
|
|
17
|
+
function toHierarchical(nodes) {
|
|
18
|
+
return nodes.map((node) => {
|
|
19
|
+
const collapsed = collapseNode(node);
|
|
20
|
+
const isFile = collapsed.originalNode.status !== undefined;
|
|
21
|
+
return {
|
|
22
|
+
displayPath: collapsed.path,
|
|
23
|
+
fullPath: collapsed.originalNode.path,
|
|
24
|
+
isFile,
|
|
25
|
+
fileIndex: collapsed.originalNode.fileIndex,
|
|
26
|
+
status: collapsed.originalNode.status,
|
|
27
|
+
additions: collapsed.originalNode.additions,
|
|
28
|
+
deletions: collapsed.originalNode.deletions,
|
|
29
|
+
children: toHierarchical(collapsed.children),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a directory tree from file paths
|
|
35
|
+
* @param files Array of file info objects
|
|
36
|
+
* @returns Array of TreeNode objects ready for rendering
|
|
37
|
+
*/
|
|
38
|
+
export function buildDirectoryTree(files) {
|
|
39
|
+
if (files.length === 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const tree = buildInternalTree(files);
|
|
43
|
+
sortInternalTree(tree);
|
|
44
|
+
return flattenTree(tree);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build internal tree structure from file paths
|
|
48
|
+
*/
|
|
49
|
+
function buildInternalTree(files) {
|
|
50
|
+
const root = [];
|
|
51
|
+
const nodeMap = new Map();
|
|
52
|
+
for (const file of files) {
|
|
53
|
+
const parts = file.path.split("/").filter((part) => part !== "");
|
|
54
|
+
let currentPath = "";
|
|
55
|
+
let currentLevel = root;
|
|
56
|
+
for (let i = 0; i < parts.length; i++) {
|
|
57
|
+
const part = parts[i];
|
|
58
|
+
currentPath = currentPath ? `${currentPath}/${part}` : part;
|
|
59
|
+
let node = nodeMap.get(currentPath);
|
|
60
|
+
if (!node) {
|
|
61
|
+
node = {
|
|
62
|
+
path: currentPath,
|
|
63
|
+
children: [],
|
|
64
|
+
};
|
|
65
|
+
nodeMap.set(currentPath, node);
|
|
66
|
+
currentLevel.push(node);
|
|
67
|
+
}
|
|
68
|
+
// On the final part, assign file info
|
|
69
|
+
if (i === parts.length - 1) {
|
|
70
|
+
node.title = part;
|
|
71
|
+
node.fileIndex = file.fileIndex;
|
|
72
|
+
node.status = file.status;
|
|
73
|
+
node.additions = file.additions;
|
|
74
|
+
node.deletions = file.deletions;
|
|
75
|
+
}
|
|
76
|
+
currentLevel = node.children;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return root;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get the base name from a path
|
|
83
|
+
*/
|
|
84
|
+
function getName(node) {
|
|
85
|
+
const parts = node.path.split("/");
|
|
86
|
+
return parts[parts.length - 1] || node.path;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Sort tree nodes by name at every level so file ordering is deterministic
|
|
90
|
+
* and independent from incoming git diff section order.
|
|
91
|
+
*/
|
|
92
|
+
function sortInternalTree(nodes) {
|
|
93
|
+
nodes.sort((a, b) => getName(a).toLowerCase().localeCompare(getName(b).toLowerCase()));
|
|
94
|
+
for (const node of nodes) {
|
|
95
|
+
if (node.children.length > 0) {
|
|
96
|
+
sortInternalTree(node.children);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Collapse directories that only contain a single subdirectory (no files)
|
|
102
|
+
*/
|
|
103
|
+
function collapseNode(node) {
|
|
104
|
+
let currentNode = node;
|
|
105
|
+
let collapsedPath = getName(currentNode);
|
|
106
|
+
// Keep collapsing while:
|
|
107
|
+
// - Current node has exactly one child
|
|
108
|
+
// - Current node is not a file (no status/title means it's a directory)
|
|
109
|
+
// - The single child is also a directory (no status)
|
|
110
|
+
while (currentNode.children.length === 1 &&
|
|
111
|
+
currentNode.status === undefined &&
|
|
112
|
+
currentNode.children[0].status === undefined &&
|
|
113
|
+
currentNode.children[0].children.length > 0) {
|
|
114
|
+
currentNode = currentNode.children[0];
|
|
115
|
+
collapsedPath = collapsedPath + "/" + getName(currentNode);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
path: collapsedPath,
|
|
119
|
+
collapsed: collapsedPath !== getName(node),
|
|
120
|
+
children: currentNode.children,
|
|
121
|
+
originalNode: currentNode,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Flatten the tree into a linear array of TreeNode objects
|
|
126
|
+
*/
|
|
127
|
+
function flattenTree(tree) {
|
|
128
|
+
const result = [];
|
|
129
|
+
function processNode(node, prefix, isLast, isRoot) {
|
|
130
|
+
const collapsed = collapseNode(node);
|
|
131
|
+
const displayPath = collapsed.path;
|
|
132
|
+
const connector = " ";
|
|
133
|
+
// Determine if this is a file (has status) or directory
|
|
134
|
+
const isFile = collapsed.originalNode.status !== undefined;
|
|
135
|
+
result.push({
|
|
136
|
+
displayPath,
|
|
137
|
+
fullPath: collapsed.originalNode.path,
|
|
138
|
+
isFile,
|
|
139
|
+
fileIndex: collapsed.originalNode.fileIndex,
|
|
140
|
+
status: collapsed.originalNode.status,
|
|
141
|
+
additions: collapsed.originalNode.additions,
|
|
142
|
+
deletions: collapsed.originalNode.deletions,
|
|
143
|
+
connector,
|
|
144
|
+
prefix,
|
|
145
|
+
});
|
|
146
|
+
// Process children
|
|
147
|
+
if (collapsed.children.length > 0) {
|
|
148
|
+
const childPrefix = prefix + " ";
|
|
149
|
+
collapsed.children.forEach((child, idx) => {
|
|
150
|
+
const childIsLast = idx === collapsed.children.length - 1;
|
|
151
|
+
processNode(child, childPrefix, childIsLast, false);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Process root level nodes
|
|
156
|
+
tree.forEach((node, idx) => {
|
|
157
|
+
const isLast = idx === tree.length - 1;
|
|
158
|
+
processNode(node, "", isLast, true);
|
|
159
|
+
});
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-tree.test.d.ts","sourceRoot":"","sources":["../src/directory-tree.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "@opentuah/react/jsx-runtime";
|
|
2
|
+
// Tests for directory tree building and rendering
|
|
3
|
+
// Uses opentui test renderer with captureCharFrame() for visual testing
|
|
4
|
+
import { testRender } from "@opentuah/react/test-utils";
|
|
5
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
6
|
+
import { DirectoryTreeView } from "./components/directory-tree-view.js";
|
|
7
|
+
import { buildDirectoryTree, buildHierarchicalTree } from "./directory-tree.js";
|
|
8
|
+
/**
|
|
9
|
+
* Simple component to render tree nodes as text for testing
|
|
10
|
+
*/
|
|
11
|
+
function TreeRenderer({ nodes }) {
|
|
12
|
+
return (_jsx("box", { style: { flexDirection: "column" }, children: nodes.map((node, idx) => {
|
|
13
|
+
// Build the line: prefix + connector + path + optional stats
|
|
14
|
+
const statsStr = node.isFile
|
|
15
|
+
? ` (+${node.additions},-${node.deletions})`
|
|
16
|
+
: "";
|
|
17
|
+
return (_jsxs("text", { children: [node.prefix, node.connector, node.displayPath, statsStr] }, idx));
|
|
18
|
+
}) }));
|
|
19
|
+
}
|
|
20
|
+
describe("buildDirectoryTree", () => {
|
|
21
|
+
it("should return empty array for no files", () => {
|
|
22
|
+
const result = buildDirectoryTree([]);
|
|
23
|
+
expect(result).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
it("should handle single file at root", () => {
|
|
26
|
+
const files = [
|
|
27
|
+
{ path: "README.md", status: "modified", additions: 5, deletions: 2 },
|
|
28
|
+
];
|
|
29
|
+
const result = buildDirectoryTree(files);
|
|
30
|
+
expect(result).toHaveLength(1);
|
|
31
|
+
expect(result[0].displayPath).toBe("README.md");
|
|
32
|
+
expect(result[0].isFile).toBe(true);
|
|
33
|
+
expect(result[0].status).toBe("modified");
|
|
34
|
+
});
|
|
35
|
+
it("should collapse single-child directories", () => {
|
|
36
|
+
const files = [
|
|
37
|
+
{ path: "src/components/Button.tsx", status: "added", additions: 50, deletions: 0 },
|
|
38
|
+
];
|
|
39
|
+
const result = buildDirectoryTree(files);
|
|
40
|
+
// Should collapse src/components into one directory node
|
|
41
|
+
expect(result).toHaveLength(2);
|
|
42
|
+
expect(result[0].displayPath).toBe("src/components");
|
|
43
|
+
expect(result[0].isFile).toBe(false);
|
|
44
|
+
expect(result[1].displayPath).toBe("Button.tsx");
|
|
45
|
+
expect(result[1].isFile).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
it("should sort nodes alphabetically regardless of input order", () => {
|
|
48
|
+
const files = [
|
|
49
|
+
{ path: "website/src/index.ts", status: "modified", additions: 1, deletions: 0 },
|
|
50
|
+
{ path: "db/schema.prisma", status: "modified", additions: 1, deletions: 0 },
|
|
51
|
+
{ path: "discord/src/utils.ts", status: "modified", additions: 1, deletions: 0 },
|
|
52
|
+
{ path: "discord/src/cli.ts", status: "modified", additions: 1, deletions: 0 },
|
|
53
|
+
{ path: "gateway-proxy/src/main.rs", status: "modified", additions: 1, deletions: 0 },
|
|
54
|
+
];
|
|
55
|
+
const result = buildDirectoryTree(files);
|
|
56
|
+
const rendered = result.map((node) => `${node.prefix}${node.connector}${node.displayPath}`);
|
|
57
|
+
expect(rendered).toEqual([
|
|
58
|
+
" db",
|
|
59
|
+
" schema.prisma",
|
|
60
|
+
" discord/src",
|
|
61
|
+
" cli.ts",
|
|
62
|
+
" utils.ts",
|
|
63
|
+
" gateway-proxy/src",
|
|
64
|
+
" main.rs",
|
|
65
|
+
" website/src",
|
|
66
|
+
" index.ts",
|
|
67
|
+
]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("TreeRenderer visual tests", () => {
|
|
71
|
+
let testSetup;
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
if (testSetup) {
|
|
74
|
+
testSetup.renderer.destroy();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
it("should render single file", async () => {
|
|
78
|
+
const files = [
|
|
79
|
+
{ path: "package.json", status: "modified", additions: 1, deletions: 1 },
|
|
80
|
+
];
|
|
81
|
+
const nodes = buildDirectoryTree(files);
|
|
82
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
83
|
+
width: 50,
|
|
84
|
+
height: 5,
|
|
85
|
+
});
|
|
86
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
87
|
+
await testSetup.renderOnce();
|
|
88
|
+
const frame = testSetup.captureCharFrame();
|
|
89
|
+
expect(frame).toMatchSnapshot();
|
|
90
|
+
});
|
|
91
|
+
it("should render multiple root files", async () => {
|
|
92
|
+
const files = [
|
|
93
|
+
{ path: "package.json", status: "modified", additions: 1, deletions: 1 },
|
|
94
|
+
{ path: "README.md", status: "added", additions: 20, deletions: 0 },
|
|
95
|
+
{ path: "tsconfig.json", status: "deleted", additions: 0, deletions: 15 },
|
|
96
|
+
];
|
|
97
|
+
const nodes = buildDirectoryTree(files);
|
|
98
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
99
|
+
width: 50,
|
|
100
|
+
height: 7,
|
|
101
|
+
});
|
|
102
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
103
|
+
await testSetup.renderOnce();
|
|
104
|
+
const frame = testSetup.captureCharFrame();
|
|
105
|
+
expect(frame).toMatchSnapshot();
|
|
106
|
+
});
|
|
107
|
+
it("should render nested directories with indentation", async () => {
|
|
108
|
+
const files = [
|
|
109
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2 },
|
|
110
|
+
{ path: "src/utils.ts", status: "added", additions: 30, deletions: 0 },
|
|
111
|
+
];
|
|
112
|
+
const nodes = buildDirectoryTree(files);
|
|
113
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
114
|
+
width: 50,
|
|
115
|
+
height: 7,
|
|
116
|
+
});
|
|
117
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
118
|
+
await testSetup.renderOnce();
|
|
119
|
+
const frame = testSetup.captureCharFrame();
|
|
120
|
+
expect(frame).toMatchSnapshot();
|
|
121
|
+
});
|
|
122
|
+
it("should collapse single-child directories", async () => {
|
|
123
|
+
const files = [
|
|
124
|
+
{ path: "src/components/Button.tsx", status: "added", additions: 50, deletions: 0 },
|
|
125
|
+
{ path: "src/components/Input.tsx", status: "added", additions: 40, deletions: 0 },
|
|
126
|
+
];
|
|
127
|
+
const nodes = buildDirectoryTree(files);
|
|
128
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
129
|
+
width: 50,
|
|
130
|
+
height: 7,
|
|
131
|
+
});
|
|
132
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
133
|
+
await testSetup.renderOnce();
|
|
134
|
+
const frame = testSetup.captureCharFrame();
|
|
135
|
+
expect(frame).toMatchSnapshot();
|
|
136
|
+
});
|
|
137
|
+
it("should render complex nested structure", async () => {
|
|
138
|
+
const files = [
|
|
139
|
+
{ path: "package.json", status: "modified", additions: 2, deletions: 1, fileIndex: 0 },
|
|
140
|
+
{ path: "src/index.ts", status: "modified", additions: 10, deletions: 5, fileIndex: 1 },
|
|
141
|
+
{ path: "src/components/Button.tsx", status: "added", additions: 50, deletions: 0, fileIndex: 2 },
|
|
142
|
+
{ path: "src/components/Input.tsx", status: "modified", additions: 15, deletions: 8, fileIndex: 3 },
|
|
143
|
+
{ path: "src/utils/helpers.ts", status: "deleted", additions: 0, deletions: 30, fileIndex: 4 },
|
|
144
|
+
{ path: "tests/index.test.ts", status: "added", additions: 25, deletions: 0, fileIndex: 5 },
|
|
145
|
+
];
|
|
146
|
+
const nodes = buildDirectoryTree(files);
|
|
147
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
148
|
+
width: 60,
|
|
149
|
+
height: 15,
|
|
150
|
+
});
|
|
151
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
152
|
+
await testSetup.renderOnce();
|
|
153
|
+
const frame = testSetup.captureCharFrame();
|
|
154
|
+
expect(frame).toMatchSnapshot();
|
|
155
|
+
});
|
|
156
|
+
it("should handle deeply nested paths with collapse", async () => {
|
|
157
|
+
const files = [
|
|
158
|
+
{ path: "packages/core/src/lib/utils/helpers.ts", status: "modified", additions: 5, deletions: 3 },
|
|
159
|
+
{ path: "packages/core/src/lib/utils/format.ts", status: "added", additions: 20, deletions: 0 },
|
|
160
|
+
];
|
|
161
|
+
const nodes = buildDirectoryTree(files);
|
|
162
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
163
|
+
width: 60,
|
|
164
|
+
height: 7,
|
|
165
|
+
});
|
|
166
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
167
|
+
await testSetup.renderOnce();
|
|
168
|
+
const frame = testSetup.captureCharFrame();
|
|
169
|
+
expect(frame).toMatchSnapshot();
|
|
170
|
+
});
|
|
171
|
+
it("should handle sibling directories at different levels", async () => {
|
|
172
|
+
const files = [
|
|
173
|
+
{ path: "src/api/routes.ts", status: "modified", additions: 10, deletions: 5 },
|
|
174
|
+
{ path: "src/api/handlers.ts", status: "added", additions: 30, deletions: 0 },
|
|
175
|
+
{ path: "src/db/models.ts", status: "modified", additions: 8, deletions: 2 },
|
|
176
|
+
{ path: "lib/utils.ts", status: "added", additions: 15, deletions: 0 },
|
|
177
|
+
];
|
|
178
|
+
const nodes = buildDirectoryTree(files);
|
|
179
|
+
testSetup = await testRender(_jsx(TreeRenderer, { nodes: nodes }), {
|
|
180
|
+
width: 60,
|
|
181
|
+
height: 12,
|
|
182
|
+
});
|
|
183
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
184
|
+
await testSetup.renderOnce();
|
|
185
|
+
const frame = testSetup.captureCharFrame();
|
|
186
|
+
expect(frame).toMatchSnapshot();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
describe("buildHierarchicalTree", () => {
|
|
190
|
+
it("should return empty array for no files", () => {
|
|
191
|
+
const result = buildHierarchicalTree([]);
|
|
192
|
+
expect(result).toEqual([]);
|
|
193
|
+
});
|
|
194
|
+
it("should handle single file at root", () => {
|
|
195
|
+
const files = [
|
|
196
|
+
{ path: "README.md", status: "modified", additions: 5, deletions: 2 },
|
|
197
|
+
];
|
|
198
|
+
const result = buildHierarchicalTree(files);
|
|
199
|
+
expect(result).toHaveLength(1);
|
|
200
|
+
expect(result[0].displayPath).toBe("README.md");
|
|
201
|
+
expect(result[0].isFile).toBe(true);
|
|
202
|
+
expect(result[0].status).toBe("modified");
|
|
203
|
+
expect(result[0].children).toEqual([]);
|
|
204
|
+
});
|
|
205
|
+
it("should build nested directory hierarchy", () => {
|
|
206
|
+
const files = [
|
|
207
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2 },
|
|
208
|
+
{ path: "src/utils.ts", status: "added", additions: 30, deletions: 0 },
|
|
209
|
+
];
|
|
210
|
+
const result = buildHierarchicalTree(files);
|
|
211
|
+
expect(result).toHaveLength(1);
|
|
212
|
+
expect(result[0].displayPath).toBe("src");
|
|
213
|
+
expect(result[0].isFile).toBe(false);
|
|
214
|
+
expect(result[0].children).toHaveLength(2);
|
|
215
|
+
expect(result[0].children[0].displayPath).toBe("index.ts");
|
|
216
|
+
expect(result[0].children[0].isFile).toBe(true);
|
|
217
|
+
expect(result[0].children[1].displayPath).toBe("utils.ts");
|
|
218
|
+
expect(result[0].children[1].isFile).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
it("should collapse single-child directories in hierarchy", () => {
|
|
221
|
+
const files = [
|
|
222
|
+
{ path: "src/components/Button.tsx", status: "added", additions: 50, deletions: 0 },
|
|
223
|
+
];
|
|
224
|
+
const result = buildHierarchicalTree(files);
|
|
225
|
+
expect(result).toHaveLength(1);
|
|
226
|
+
expect(result[0].displayPath).toBe("src/components");
|
|
227
|
+
expect(result[0].isFile).toBe(false);
|
|
228
|
+
expect(result[0].children).toHaveLength(1);
|
|
229
|
+
expect(result[0].children[0].displayPath).toBe("Button.tsx");
|
|
230
|
+
expect(result[0].children[0].isFile).toBe(true);
|
|
231
|
+
});
|
|
232
|
+
it("should sort nodes alphabetically at every level", () => {
|
|
233
|
+
const files = [
|
|
234
|
+
{ path: "b/file.ts", status: "modified", additions: 1, deletions: 0 },
|
|
235
|
+
{ path: "a/file.ts", status: "modified", additions: 1, deletions: 0 },
|
|
236
|
+
];
|
|
237
|
+
const result = buildHierarchicalTree(files);
|
|
238
|
+
expect(result).toHaveLength(2);
|
|
239
|
+
expect(result[0].displayPath).toBe("a");
|
|
240
|
+
expect(result[1].displayPath).toBe("b");
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
describe("DirectoryTreeView component", () => {
|
|
244
|
+
let testSetup;
|
|
245
|
+
afterEach(() => {
|
|
246
|
+
if (testSetup) {
|
|
247
|
+
testSetup.renderer.destroy();
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
it("renders folder icon on directory rows", async () => {
|
|
251
|
+
const files = [
|
|
252
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
253
|
+
];
|
|
254
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github" }), { width: 60, height: 5 });
|
|
255
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
256
|
+
await testSetup.renderOnce();
|
|
257
|
+
const frame = testSetup.captureCharFrame();
|
|
258
|
+
expect(frame).toContain("");
|
|
259
|
+
});
|
|
260
|
+
it("renders file-type icon on file rows", async () => {
|
|
261
|
+
const files = [
|
|
262
|
+
{ path: "README.md", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
263
|
+
{ path: "src/index.ts", status: "added", additions: 30, deletions: 0, fileIndex: 1 },
|
|
264
|
+
];
|
|
265
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github" }), { width: 60, height: 5 });
|
|
266
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
267
|
+
await testSetup.renderOnce();
|
|
268
|
+
const frame = testSetup.captureCharFrame();
|
|
269
|
+
expect(frame).toContain("");
|
|
270
|
+
expect(frame).toContain("");
|
|
271
|
+
});
|
|
272
|
+
it("renders generic file icon for unknown extensions", async () => {
|
|
273
|
+
const files = [
|
|
274
|
+
{ path: "config.yaml", status: "modified", additions: 1, deletions: 0, fileIndex: 0 },
|
|
275
|
+
];
|
|
276
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github" }), { width: 60, height: 3 });
|
|
277
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
278
|
+
await testSetup.renderOnce();
|
|
279
|
+
const frame = testSetup.captureCharFrame();
|
|
280
|
+
expect(frame).toContain("");
|
|
281
|
+
});
|
|
282
|
+
it("should render tree without border", async () => {
|
|
283
|
+
const files = [
|
|
284
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
285
|
+
{ path: "src/utils.ts", status: "added", additions: 30, deletions: 0, fileIndex: 1 },
|
|
286
|
+
{ path: "README.md", status: "deleted", additions: 0, deletions: 15, fileIndex: 2 },
|
|
287
|
+
];
|
|
288
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github" }), { width: 60, height: 12 });
|
|
289
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
290
|
+
await testSetup.renderOnce();
|
|
291
|
+
const frame = testSetup.captureCharFrame();
|
|
292
|
+
expect(frame).toMatchSnapshot();
|
|
293
|
+
});
|
|
294
|
+
it("truncates long file paths while preserving the filename", async () => {
|
|
295
|
+
const files = [
|
|
296
|
+
{
|
|
297
|
+
path: "packages/core/src/lib/really-long-file-name.ts",
|
|
298
|
+
status: "modified",
|
|
299
|
+
additions: 12,
|
|
300
|
+
deletions: 3,
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", width: 28 }), { width: 28, height: 4 });
|
|
304
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
305
|
+
await testSetup.renderOnce();
|
|
306
|
+
const frame = testSetup.captureCharFrame();
|
|
307
|
+
expect(frame).toContain("…");
|
|
308
|
+
expect(frame).toContain("name.ts");
|
|
309
|
+
expect(frame).toContain("(+12,-3)");
|
|
310
|
+
});
|
|
311
|
+
it("should render empty when no files", async () => {
|
|
312
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: [], themeName: "github" }), { width: 40, height: 5 });
|
|
313
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
314
|
+
await testSetup.renderOnce();
|
|
315
|
+
const frame = testSetup.captureCharFrame();
|
|
316
|
+
// Should render nothing (DirectoryTreeView returns null for empty)
|
|
317
|
+
expect(frame).toMatchSnapshot();
|
|
318
|
+
});
|
|
319
|
+
it("should highlight the active file row", async () => {
|
|
320
|
+
const files = [
|
|
321
|
+
{ path: "README.md", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
322
|
+
{ path: "src/index.ts", status: "added", additions: 30, deletions: 0, fileIndex: 1 },
|
|
323
|
+
{ path: "src/utils.ts", status: "deleted", additions: 0, deletions: 15, fileIndex: 2 },
|
|
324
|
+
];
|
|
325
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", activeFileIndex: 1 }), { width: 60, height: 12 });
|
|
326
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
327
|
+
await testSetup.renderOnce();
|
|
328
|
+
const frame = testSetup.captureCharFrame();
|
|
329
|
+
// Should show the tree with the active file highlighted
|
|
330
|
+
expect(frame).toContain("README.md");
|
|
331
|
+
expect(frame).toContain("index.ts");
|
|
332
|
+
expect(frame).toContain("utils.ts");
|
|
333
|
+
});
|
|
334
|
+
it("filters out children of collapsed folders", async () => {
|
|
335
|
+
const files = [
|
|
336
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
337
|
+
{ path: "src/utils.ts", status: "added", additions: 30, deletions: 0, fileIndex: 1 },
|
|
338
|
+
];
|
|
339
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", initialCollapsedPaths: ["src"] }), { width: 60, height: 5 });
|
|
340
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
341
|
+
await testSetup.renderOnce();
|
|
342
|
+
const frame = testSetup.captureCharFrame();
|
|
343
|
+
expect(frame).toContain("src");
|
|
344
|
+
expect(frame).not.toContain("index.ts");
|
|
345
|
+
expect(frame).not.toContain("utils.ts");
|
|
346
|
+
});
|
|
347
|
+
it("shows closed folder icon for collapsed folders", async () => {
|
|
348
|
+
const files = [
|
|
349
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
350
|
+
];
|
|
351
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", initialCollapsedPaths: ["src"] }), { width: 60, height: 3 });
|
|
352
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
353
|
+
await testSetup.renderOnce();
|
|
354
|
+
const frame = testSetup.captureCharFrame();
|
|
355
|
+
expect(frame).toContain("");
|
|
356
|
+
expect(frame).not.toContain("");
|
|
357
|
+
});
|
|
358
|
+
it("collapsing compressed tree path hides entire chain", async () => {
|
|
359
|
+
const files = [
|
|
360
|
+
{ path: "src/components/Button.tsx", status: "added", additions: 50, deletions: 0, fileIndex: 0 },
|
|
361
|
+
{ path: "src/components/Input.tsx", status: "modified", additions: 10, deletions: 5, fileIndex: 1 },
|
|
362
|
+
];
|
|
363
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", initialCollapsedPaths: ["src/components"] }), { width: 60, height: 5 });
|
|
364
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
365
|
+
await testSetup.renderOnce();
|
|
366
|
+
const frame = testSetup.captureCharFrame();
|
|
367
|
+
expect(frame).toContain("src/components");
|
|
368
|
+
expect(frame).not.toContain("Button.tsx");
|
|
369
|
+
expect(frame).not.toContain("Input.tsx");
|
|
370
|
+
});
|
|
371
|
+
it("renders with active file inside collapsed folder", async () => {
|
|
372
|
+
const files = [
|
|
373
|
+
{ path: "src/index.ts", status: "modified", additions: 5, deletions: 2, fileIndex: 0 },
|
|
374
|
+
];
|
|
375
|
+
testSetup = await testRender(_jsx(DirectoryTreeView, { files: files, themeName: "github", initialCollapsedPaths: ["src"], activeFileIndex: 0 }), { width: 60, height: 3 });
|
|
376
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = false;
|
|
377
|
+
await testSetup.renderOnce();
|
|
378
|
+
const frame = testSetup.captureCharFrame();
|
|
379
|
+
// Folder is visible, file is hidden
|
|
380
|
+
expect(frame).toContain("src");
|
|
381
|
+
expect(frame).not.toContain("index.ts");
|
|
382
|
+
});
|
|
383
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import { type ResolvedTheme } from "./themes";
|
|
3
|
+
export interface DropdownOption {
|
|
4
|
+
title: string;
|
|
5
|
+
value: string;
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
keywords?: string[];
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface DropdownProps {
|
|
11
|
+
id?: string;
|
|
12
|
+
tooltip?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
selectedValues?: string[];
|
|
15
|
+
itemsPerPage?: number;
|
|
16
|
+
options: DropdownOption[];
|
|
17
|
+
onChange?: (newValue: string) => void;
|
|
18
|
+
onFocus?: (value: string) => void;
|
|
19
|
+
onEscape?: () => void;
|
|
20
|
+
theme: ResolvedTheme;
|
|
21
|
+
}
|
|
22
|
+
export declare function filterDropdownOptions(options: DropdownOption[], searchText: string): DropdownOption[];
|
|
23
|
+
declare const Dropdown: (props: DropdownProps) => React.ReactNode;
|
|
24
|
+
export default Dropdown;
|
|
25
|
+
export { Dropdown };
|
|
26
|
+
//# sourceMappingURL=dropdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropdown.d.ts","sourceRoot":"","sources":["../src/dropdown.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAA4C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxF,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,UAAU,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,cAAc,EAAE,CAerG;AAED,QAAA,MAAM,QAAQ,GAAI,OAAO,aAAa,KAAG,KAAK,CAAC,SA+M9C,CAAC;AAqFF,eAAe,QAAQ,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|