@djangocfg/ui-tools 2.1.415 → 2.1.417

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/audio-player/index.cjs +2099 -0
  2. package/dist/audio-player/index.cjs.map +1 -0
  3. package/dist/audio-player/index.css +65 -0
  4. package/dist/audio-player/index.css.map +1 -0
  5. package/dist/audio-player/index.d.cts +174 -0
  6. package/dist/audio-player/index.d.ts +174 -0
  7. package/dist/audio-player/index.mjs +2076 -0
  8. package/dist/audio-player/index.mjs.map +1 -0
  9. package/dist/composer-registry/index.cjs +45 -0
  10. package/dist/composer-registry/index.cjs.map +1 -0
  11. package/dist/composer-registry/index.d.cts +73 -0
  12. package/dist/composer-registry/index.d.ts +73 -0
  13. package/dist/composer-registry/index.mjs +39 -0
  14. package/dist/composer-registry/index.mjs.map +1 -0
  15. package/dist/file-icon/index.d.cts +1 -1
  16. package/dist/file-icon/index.d.ts +1 -1
  17. package/dist/slots-ClRpIzoh.d.cts +88 -0
  18. package/dist/slots-ClRpIzoh.d.ts +88 -0
  19. package/dist/tree/index.cjs +2019 -279
  20. package/dist/tree/index.cjs.map +1 -1
  21. package/dist/tree/index.d.cts +731 -72
  22. package/dist/tree/index.d.ts +731 -72
  23. package/dist/tree/index.mjs +2009 -282
  24. package/dist/tree/index.mjs.map +1 -1
  25. package/package.json +18 -9
  26. package/src/tools/chat/README.md +111 -1
  27. package/src/tools/chat/composer/Composer.tsx +146 -25
  28. package/src/tools/chat/composer/ComposerRichTextarea.tsx +25 -0
  29. package/src/tools/chat/composer/index.ts +22 -0
  30. package/src/tools/chat/composer/slash/README.md +187 -0
  31. package/src/tools/chat/composer/slash/SlashHighlightTextarea.tsx +144 -0
  32. package/src/tools/chat/composer/slash/SlashMenu.tsx +142 -0
  33. package/src/tools/chat/composer/slash/SlashToken.tsx +57 -0
  34. package/src/tools/chat/composer/slash/index.ts +44 -0
  35. package/src/tools/chat/composer/slash/labels.ts +19 -0
  36. package/src/tools/chat/composer/slash/state.ts +168 -0
  37. package/src/tools/chat/composer/slash/types.ts +64 -0
  38. package/src/tools/chat/composer/slash/useSlashCommands.ts +204 -0
  39. package/src/tools/chat/composer/types.ts +8 -0
  40. package/src/tools/chat/context/ChatProvider.tsx +13 -78
  41. package/src/tools/chat/hooks/useAutoFocusOnStreamEnd.ts +12 -15
  42. package/src/tools/chat/hooks/useFocusOnEmptyClick.ts +4 -5
  43. package/src/tools/chat/launcher/header/ChatHeader.tsx +14 -19
  44. package/src/tools/chat/launcher/header/ChatHeaderActionButton.tsx +8 -12
  45. package/src/tools/chat/shell/SuggestedPrompts.tsx +194 -0
  46. package/src/tools/chat/shell/index.ts +6 -0
  47. package/src/tools/data/Listbox/lazy.tsx +1 -1
  48. package/src/tools/data/Masonry/lazy.tsx +1 -1
  49. package/src/tools/data/Timeline/lazy.tsx +1 -1
  50. package/src/tools/data/Tree/FinderTree.tsx +42 -0
  51. package/src/tools/data/Tree/README.md +337 -208
  52. package/src/tools/data/Tree/TreeDndProvider.tsx +137 -0
  53. package/src/tools/data/Tree/TreeRoot.tsx +111 -72
  54. package/src/tools/data/Tree/__tests__/dnd.test.ts +160 -0
  55. package/src/tools/data/Tree/__tests__/keyboard.test.ts +137 -0
  56. package/src/tools/data/Tree/__tests__/renameUtils.test.ts +52 -0
  57. package/src/tools/data/Tree/__tests__/selection.test.ts +227 -0
  58. package/src/tools/data/Tree/components/TreeDropIndicator.tsx +65 -0
  59. package/src/tools/data/Tree/components/TreeEmptyArea.tsx +160 -0
  60. package/src/tools/data/Tree/components/TreeRenameInput.tsx +114 -0
  61. package/src/tools/data/Tree/components/TreeRow.tsx +103 -8
  62. package/src/tools/data/Tree/components/index.ts +6 -0
  63. package/src/tools/data/Tree/context/TreeContext.tsx +223 -363
  64. package/src/tools/data/Tree/context/TreeContextValue.ts +139 -0
  65. package/src/tools/data/Tree/context/async-children/collect-ids.ts +27 -0
  66. package/src/tools/data/Tree/context/async-children/index.ts +8 -0
  67. package/src/tools/data/Tree/context/async-children/use-async-children.ts +157 -0
  68. package/src/tools/data/Tree/context/clipboard/index.ts +4 -0
  69. package/src/tools/data/Tree/context/clipboard/use-clipboard.ts +115 -0
  70. package/src/tools/data/Tree/context/dnd/index.ts +8 -0
  71. package/src/tools/data/Tree/context/dnd/use-dnd.ts +194 -0
  72. package/src/tools/data/Tree/context/expansion/index.ts +4 -0
  73. package/src/tools/data/Tree/context/expansion/use-expansion.ts +55 -0
  74. package/src/tools/data/Tree/context/hooks.ts +68 -1
  75. package/src/tools/data/Tree/context/index.ts +3 -0
  76. package/src/tools/data/Tree/context/menu/builtin-actions.ts +357 -0
  77. package/src/tools/data/Tree/context/menu/index.ts +11 -0
  78. package/src/tools/data/Tree/context/menu/render.tsx +75 -0
  79. package/src/tools/data/Tree/context/menu/use-resolved-menu.ts +141 -0
  80. package/src/tools/data/Tree/context/persist/index.ts +4 -0
  81. package/src/tools/data/Tree/context/persist/use-persist-sync.ts +74 -0
  82. package/src/tools/data/Tree/context/rename/index.ts +4 -0
  83. package/src/tools/data/Tree/context/rename/use-rename.ts +113 -0
  84. package/src/tools/data/Tree/context/selection/index.ts +4 -0
  85. package/src/tools/data/Tree/context/selection/use-selection.ts +146 -0
  86. package/src/tools/data/Tree/context/state/index.ts +6 -0
  87. package/src/tools/data/Tree/context/state/initial.ts +41 -0
  88. package/src/tools/data/Tree/context/state/reducer.ts +76 -0
  89. package/src/tools/data/Tree/context/state/types.ts +46 -0
  90. package/src/tools/data/Tree/data/clipboard.ts +33 -0
  91. package/src/tools/data/Tree/data/dnd.ts +123 -0
  92. package/src/tools/data/Tree/data/finderShortcuts.ts +67 -0
  93. package/src/tools/data/Tree/data/index.ts +19 -0
  94. package/src/tools/data/Tree/data/renameUtils.ts +51 -0
  95. package/src/tools/data/Tree/data/selection.ts +157 -0
  96. package/src/tools/data/Tree/hooks/finder-hotkeys/build-ctx.ts +48 -0
  97. package/src/tools/data/Tree/hooks/finder-hotkeys/index.ts +8 -0
  98. package/src/tools/data/Tree/hooks/finder-hotkeys/use-tree-finder-hotkeys.ts +166 -0
  99. package/src/tools/data/Tree/hooks/index.ts +23 -4
  100. package/src/tools/data/Tree/hooks/keyboard/activation.ts +27 -0
  101. package/src/tools/data/Tree/hooks/keyboard/arrow-nav.ts +26 -0
  102. package/src/tools/data/Tree/hooks/keyboard/expand-collapse.ts +54 -0
  103. package/src/tools/data/Tree/hooks/keyboard/index.ts +10 -0
  104. package/src/tools/data/Tree/hooks/keyboard/types.ts +39 -0
  105. package/src/tools/data/Tree/hooks/keyboard/use-tree-keyboard.ts +196 -0
  106. package/src/tools/data/Tree/hooks/type-ahead/index.ts +5 -0
  107. package/src/tools/data/Tree/hooks/type-ahead/match-prefix.ts +42 -0
  108. package/src/tools/data/Tree/hooks/{useTreeTypeAhead.ts → type-ahead/use-tree-type-ahead.ts} +8 -19
  109. package/src/tools/data/Tree/index.tsx +26 -2
  110. package/src/tools/data/Tree/types/activation.ts +30 -0
  111. package/src/tools/data/Tree/types/adapter.ts +70 -0
  112. package/src/tools/data/Tree/types/index.ts +27 -0
  113. package/src/tools/data/Tree/types/labels.ts +97 -0
  114. package/src/tools/data/Tree/types/loader.ts +9 -0
  115. package/src/tools/data/Tree/types/node.ts +38 -0
  116. package/src/tools/data/Tree/types/root-props.ts +158 -0
  117. package/src/tools/data/Tree/types/selection.ts +3 -0
  118. package/src/tools/data/Tree/types/slots.ts +64 -0
  119. package/src/tools/dev/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +6 -9
  120. package/src/tools/dev/OpenapiViewer/components/DocsLayout/index.tsx +2 -4
  121. package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +85 -0
  122. package/src/tools/forms/MarkdownEditor/index.ts +1 -0
  123. package/src/tools/forms/MarkdownEditor/lazy.tsx +6 -0
  124. package/src/tools/forms/MarkdownEditor/slash/SlashCommandNode.ts +162 -0
  125. package/src/tools/forms/MarkdownEditor/slash/index.ts +4 -0
  126. package/src/tools/forms/MarkdownEditor/slash/syncSlashNode.ts +97 -0
  127. package/src/tools/forms/MarkdownEditor/slash/types.ts +13 -0
  128. package/src/tools/forms/MarkdownEditor/styles.css +18 -0
  129. package/src/tools/input/SpeechRecognition/widgets/VoiceComposerSlot.tsx +11 -12
  130. package/src/tools/integration/ComposerRegistry/index.ts +105 -0
  131. package/src/tools/media/AudioPlayer/Player.tsx +2 -0
  132. package/src/tools/media/AudioPlayer/PlayerShell.tsx +37 -22
  133. package/src/tools/media/AudioPlayer/lazy.tsx +30 -42
  134. package/src/tools/media/AudioPlayer/parts/Controls/IconButton.tsx +10 -11
  135. package/src/tools/media/AudioPlayer/parts/Controls/VolumeControl.tsx +52 -115
  136. package/src/tools/media/AudioPlayer/types.ts +15 -0
  137. package/dist/types-j2vhn4Kv.d.cts +0 -241
  138. package/dist/types-j2vhn4Kv.d.ts +0 -241
  139. package/src/tools/data/Tree/hooks/useTreeKeyboard.ts +0 -171
  140. package/src/tools/data/Tree/types.ts +0 -217
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { cn } from '@djangocfg/ui-core/lib';
5
+
6
+ import { autoSelectRange } from '../data/renameUtils';
7
+
8
+ export interface TreeRenameInputProps {
9
+ initialValue: string;
10
+ isFolder: boolean;
11
+ /** Called with the new (trimmed) name when the user presses Enter / blurs. */
12
+ onCommit: (nextName: string) => void | Promise<unknown>;
13
+ /** Called when the user presses Escape. */
14
+ onCancel: () => void;
15
+ className?: string;
16
+ }
17
+
18
+ /**
19
+ * Inline rename input rendered in place of `<TreeLabel>` while a row is
20
+ * being renamed. Mounts focused with the *base* portion of the name
21
+ * pre-selected (Finder behaviour — `foo.txt` selects `foo`).
22
+ *
23
+ * Behaviour:
24
+ * - Enter → commit
25
+ * - Escape → cancel (no adapter call)
26
+ * - blur → commit (matches Finder; intentional even for empty
27
+ * names — the host validates and re-opens on error)
28
+ * - all other keys are stopped from bubbling so Tree's container
29
+ * hotkeys (↑↓ delete F2 etc.) don't fire while typing.
30
+ */
31
+ export function TreeRenameInput({
32
+ initialValue,
33
+ isFolder,
34
+ onCommit,
35
+ onCancel,
36
+ className,
37
+ }: TreeRenameInputProps) {
38
+ const [value, setValue] = useState(initialValue);
39
+ const inputRef = useRef<HTMLInputElement>(null);
40
+ // Track whether we already committed/cancelled so the blur handler
41
+ // doesn't fire a second time after Enter/Escape.
42
+ const settledRef = useRef(false);
43
+
44
+ useEffect(() => {
45
+ const el = inputRef.current;
46
+ if (!el) return;
47
+ el.focus();
48
+ const [start, end] = autoSelectRange(initialValue, isFolder);
49
+ // setSelectionRange on a freshly focused input — schedule on next
50
+ // tick to dodge browser quirks where focus() resets the selection.
51
+ requestAnimationFrame(() => {
52
+ try {
53
+ el.setSelectionRange(start, end);
54
+ } catch {
55
+ /* Some input types reject setSelectionRange — safe to ignore. */
56
+ }
57
+ });
58
+ // Run only once on mount.
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, []);
61
+
62
+ const commit = () => {
63
+ if (settledRef.current) return;
64
+ settledRef.current = true;
65
+ void onCommit(value);
66
+ };
67
+
68
+ const cancel = () => {
69
+ if (settledRef.current) return;
70
+ settledRef.current = true;
71
+ onCancel();
72
+ };
73
+
74
+ return (
75
+ <input
76
+ ref={inputRef}
77
+ type="text"
78
+ value={value}
79
+ // Tree's container hotkeys (delete / arrows / F2 …) must not fire
80
+ // while the user is typing the new name.
81
+ onKeyDown={(e) => {
82
+ e.stopPropagation();
83
+ if (e.key === 'Enter') {
84
+ e.preventDefault();
85
+ commit();
86
+ } else if (e.key === 'Escape') {
87
+ e.preventDefault();
88
+ cancel();
89
+ }
90
+ }}
91
+ onChange={(e) => setValue(e.target.value)}
92
+ onBlur={commit}
93
+ // Prevent click/dblclick on the row from re-firing while the input
94
+ // is mounted (otherwise a focused click commits + re-selects).
95
+ onClick={(e) => e.stopPropagation()}
96
+ onDoubleClick={(e) => e.stopPropagation()}
97
+ onMouseDown={(e) => e.stopPropagation()}
98
+ // Right-click inside the input should be the native input menu,
99
+ // not the row's context menu.
100
+ onContextMenu={(e) => e.stopPropagation()}
101
+ className={cn(
102
+ 'min-w-0 flex-1 rounded-sm border border-primary/50 bg-background',
103
+ 'px-1 py-0 text-foreground outline-none',
104
+ 'focus:ring-1 focus:ring-primary/40',
105
+ className,
106
+ )}
107
+ style={{
108
+ // Match the row's font metrics so the input doesn't visibly jolt.
109
+ fontSize: 'var(--tree-font-size)',
110
+ height: 'calc(var(--tree-row-height) - 4px)',
111
+ }}
112
+ />
113
+ );
114
+ }
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
 
3
3
  import { Loader2 } from 'lucide-react';
4
- import { memo } from 'react';
4
+ import { memo, useCallback } from 'react';
5
+ import { useDraggable, useDroppable } from '@dnd-kit/core';
5
6
  import { cn } from '@djangocfg/ui-core/lib';
6
7
 
7
8
  import { useTreeContext } from '../context/TreeContext';
@@ -9,9 +10,11 @@ import { radiusClass, rowStateClasses } from '../data/appearance';
9
10
  import type { FlatRow, TreeRowRenderProps } from '../types';
10
11
  import { TreeChevron } from './TreeChevron';
11
12
  import { treeRowDomId } from './TreeContent';
13
+ import { TreeDropIndicator } from './TreeDropIndicator';
12
14
  import { TreeIcon } from './TreeIcon';
13
15
  import { TreeIndentGuides } from './TreeIndentGuides';
14
16
  import { TreeLabel } from './TreeLabel';
17
+ import { TreeRenameInput } from './TreeRenameInput';
15
18
 
16
19
  export interface TreeRowProps<T> {
17
20
  row: FlatRow<T>;
@@ -36,6 +39,7 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
36
39
  matchingIds,
37
40
  select,
38
41
  setSelectedIds,
42
+ clickSelect,
39
43
  toggle,
40
44
  setFocus,
41
45
  activate,
@@ -44,6 +48,11 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
44
48
  renderLabel,
45
49
  renderActions,
46
50
  renderContextMenu,
51
+ renamingId,
52
+ commitRename,
53
+ cancelRename,
54
+ clipboard,
55
+ dnd,
47
56
  } = ctx;
48
57
 
49
58
  const { node, level, isFolder, isExpanded, isLoading, posInSet, setSize } = row;
@@ -51,6 +60,8 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
51
60
  const isFocused = focused === node.id;
52
61
  const isMatchingSearch = matchingIds.has(node.id);
53
62
  const isMultiSelect = ctx.selectionMode === 'multiple';
63
+ const isCut =
64
+ clipboard?.kind === 'cut' && clipboard.ids.includes(node.id);
54
65
 
55
66
  const slot: TreeRowRenderProps<T> = {
56
67
  node,
@@ -68,33 +79,82 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
68
79
  // single-click → click activates {preview:false}
69
80
  // double-click → click only selects; dblclick activates {preview:false}
70
81
  // single-click-preview → click activates {preview:true}; dblclick activates {preview:false}
82
+ const isRenaming = renamingId === node.id;
83
+ const isDragging = dnd.draggingIds.has(node.id);
84
+ const dropTarget = dnd.dropTarget;
85
+ const isDropTarget = dropTarget?.id === node.id;
86
+ const dropPosition = isDropTarget ? dropTarget!.position : null;
87
+
88
+ // ─── DnD wiring ──────────────────────────────────────────────────
89
+ // Hooks are called unconditionally so we don't violate rules-of-
90
+ // hooks; the `disabled` flag short-circuits @dnd-kit when DnD is off
91
+ // or the row is in inline rename.
92
+ //
93
+ // Drop-zone resolution (before/inside/after) is centralised in
94
+ // `<TreeDndProvider>` via `onDragMove` — rows don't need a
95
+ // `onPointerMove` of their own. Saves a listener × N rows.
96
+ const dndDisabled = !dnd.active || isRenaming || node.disabled;
97
+ const draggable = useDraggable({ id: node.id, disabled: dndDisabled });
98
+ const droppable = useDroppable({ id: node.id, disabled: dndDisabled });
99
+
100
+ const setRowEl = useCallback(
101
+ (el: HTMLDivElement | null) => {
102
+ draggable.setNodeRef(el);
103
+ droppable.setNodeRef(el);
104
+ },
105
+ [draggable, droppable],
106
+ );
107
+
108
+ const isAllowedDrop =
109
+ dropPosition && !isDragging
110
+ ? dnd.isAllowedDrop(node, dropPosition)
111
+ : true;
71
112
  const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
72
- if (node.disabled) return;
113
+ if (node.disabled || isRenaming) return;
73
114
  setFocus(node.id);
74
- // Multi-select: a plain click replaces the selection; ⌘/Ctrl-click
75
- // toggles the clicked row (classic file-manager / VSCode behaviour).
76
- if (isMultiSelect && !(e.metaKey || e.ctrlKey)) {
77
- setSelectedIds([node.id]);
115
+ // Multi-select: full Finder/Explorer semantics plain replaces, meta
116
+ // toggles, shift extends range from anchor, shift+meta unions range.
117
+ // Single-select: clickSelect collapses to {id}. None: no-op.
118
+ if (isMultiSelect) {
119
+ clickSelect(node.id, { shift: e.shiftKey, meta: e.metaKey || e.ctrlKey });
78
120
  } else {
79
121
  select(node.id);
80
122
  }
81
123
  if (isFolder) {
124
+ // Don't toggle on shift/meta clicks — those are pure selection edits.
125
+ if (e.shiftKey || e.metaKey || e.ctrlKey) return;
82
126
  toggle(node.id);
83
127
  } else if (activationMode === 'single-click') {
128
+ // Selection-only modifier clicks should not activate the leaf.
129
+ if (e.shiftKey || e.metaKey || e.ctrlKey) return;
84
130
  activate(node, { preview: false });
85
131
  } else if (activationMode === 'single-click-preview') {
132
+ if (e.shiftKey || e.metaKey || e.ctrlKey) return;
86
133
  activate(node, { preview: true });
87
134
  }
88
135
  };
89
136
 
90
137
  const handleDoubleClick = () => {
91
- if (node.disabled) return;
138
+ if (node.disabled || isRenaming) return;
92
139
  if (isFolder) return;
93
140
  activate(node, { preview: false });
94
141
  };
95
142
 
143
+ // Finder/Explorer semantics: right-click on an unselected row switches
144
+ // selection to that single row (so menu actions apply to it). Right-
145
+ // click on a row already in the selection leaves the multi-selection
146
+ // intact (so destructive bulk actions stay safe).
147
+ const handleContextMenu = () => {
148
+ if (node.disabled || isRenaming) return;
149
+ setFocus(node.id);
150
+ if (!isSelected) {
151
+ setSelectedIds([node.id]);
152
+ }
153
+ };
154
+
96
155
  const trigger = (
97
156
  <div
157
+ ref={dnd.active ? setRowEl : undefined}
98
158
  id={treeRowDomId(node.id)}
99
159
  role="treeitem"
100
160
  aria-level={level + 1}
@@ -107,6 +167,8 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
107
167
  data-id={node.id}
108
168
  data-activation-mode={activationMode}
109
169
  data-selected={isSelected ? 'true' : undefined}
170
+ data-clipboard={isCut ? 'cut' : undefined}
171
+ data-dragging={isDragging ? 'true' : undefined}
110
172
  data-focused={isFocused && !isSelected ? 'true' : undefined}
111
173
  data-folder={isFolder || undefined}
112
174
  data-expanded={isExpanded || undefined}
@@ -116,8 +178,22 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
116
178
  height: 'var(--tree-row-height)',
117
179
  gap: 'var(--tree-gap)',
118
180
  }}
181
+ {...(dnd.active ? draggable.listeners : {})}
182
+ {...(dnd.active ? draggable.attributes : {})}
183
+ // Block the browser's native "click moves focus into the
184
+ // focusable target" behaviour. Tree owns its own focus model
185
+ // (arrow keys drive `setFocus`, click only selects + activates),
186
+ // and stealing focus on every click stops downstream auto-focus
187
+ // patterns from working — e.g. clicking a file in a tree-plus-
188
+ // preview layout would let the row grab focus after the preview
189
+ // viewer tries to take it. The synthetic `onClick` below still
190
+ // fires; only the focus side-effect of mousedown is suppressed.
191
+ onMouseDown={(e) => {
192
+ if (e.button === 0) e.preventDefault();
193
+ }}
119
194
  onClick={handleClick}
120
195
  onDoubleClick={handleDoubleClick}
196
+ onContextMenu={handleContextMenu}
121
197
  onFocus={() => setFocus(node.id)}
122
198
  className={cn(
123
199
  'group/row relative flex w-full select-none items-center pr-2 text-left',
@@ -127,6 +203,8 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
127
203
  rowStateClasses(appearance),
128
204
  'focus-visible:ring-1 focus-visible:ring-ring/50',
129
205
  isMatchingSearch && 'ring-1 ring-primary/30',
206
+ isCut && 'opacity-60',
207
+ isDragging && 'opacity-40',
130
208
  node.disabled && 'opacity-50',
131
209
  className,
132
210
  )}
@@ -142,6 +220,16 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
142
220
  />
143
221
  ) : null}
144
222
 
223
+ {/* DnD drop indicator — top/bottom line for sibling reorder,
224
+ fill for "drop into folder". */}
225
+ {dropPosition && !isDragging ? (
226
+ <TreeDropIndicator
227
+ position={dropPosition}
228
+ indent={6 + level * appearance.indent}
229
+ invalid={!isAllowedDrop}
230
+ />
231
+ ) : null}
232
+
145
233
  {showIndentGuides && level > 0 ? (
146
234
  <TreeIndentGuides level={level} indent={appearance.indent} />
147
235
  ) : null}
@@ -165,7 +253,14 @@ function TreeRowRaw<T>({ row, className }: TreeRowProps<T>) {
165
253
  className="flex min-w-0 flex-1 items-center"
166
254
  style={{ gap: 'var(--tree-gap)' }}
167
255
  >
168
- {renderLabel ? (
256
+ {renamingId === node.id ? (
257
+ <TreeRenameInput
258
+ initialValue={getItemName(node)}
259
+ isFolder={isFolder}
260
+ onCommit={(next) => commitRename(node.id, next)}
261
+ onCancel={cancelRename}
262
+ />
263
+ ) : renderLabel ? (
169
264
  renderLabel(slot)
170
265
  ) : (
171
266
  <TreeLabel isMatchingSearch={isMatchingSearch}>{getItemName(node)}</TreeLabel>
@@ -20,3 +20,9 @@ export { TreeError } from './TreeError';
20
20
  export type { TreeErrorProps } from './TreeError';
21
21
  export { TreeIndentGuides } from './TreeIndentGuides';
22
22
  export type { TreeIndentGuidesProps } from './TreeIndentGuides';
23
+ export { TreeRenameInput } from './TreeRenameInput';
24
+ export type { TreeRenameInputProps } from './TreeRenameInput';
25
+ export { TreeDropIndicator } from './TreeDropIndicator';
26
+ export type { TreeDropIndicatorProps } from './TreeDropIndicator';
27
+ export { TreeEmptyArea } from './TreeEmptyArea';
28
+ export type { TreeEmptyAreaProps } from './TreeEmptyArea';