@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
@@ -3,7 +3,9 @@
3
3
  import { useCallback, useRef } from 'react';
4
4
  import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';
5
5
 
6
- import { useChatContextOptional, type ComposerHandle } from '../context';
6
+ import { getActiveComposer } from '@djangocfg/ui-tools/composer-registry';
7
+
8
+ import { useChatContextOptional } from '../context';
7
9
  import type { Focusable } from './useAutoFocusOnStreamEnd';
8
10
 
9
11
  export interface UseFocusOnEmptyClickOptions {
@@ -51,9 +53,6 @@ export function useFocusOnEmptyClick(
51
53
  const { targetRef, enabled = true, skipWhileStreaming = true } = options;
52
54
  const ctx = useChatContextOptional();
53
55
 
54
- const composerHandleRef = useRef<ComposerHandle | null>(null);
55
- composerHandleRef.current = ctx?.composer ?? null;
56
-
57
56
  const isStreamingRef = useRef(false);
58
57
  isStreamingRef.current = ctx?.isStreaming ?? false;
59
58
 
@@ -83,7 +82,7 @@ export function useFocusOnEmptyClick(
83
82
  explicit.focus?.();
84
83
  return;
85
84
  }
86
- composerHandleRef.current?.focus?.();
85
+ getActiveComposer()?.focus?.();
87
86
  },
88
87
  [enabled, skipWhileStreaming, targetRef],
89
88
  );
@@ -7,7 +7,6 @@ import {
7
7
  Button,
8
8
  Tooltip,
9
9
  TooltipContent,
10
- TooltipProvider,
11
10
  TooltipTrigger,
12
11
  } from '@djangocfg/ui-core/components';
13
12
  import { cn } from '@djangocfg/ui-core/lib';
@@ -66,24 +65,20 @@ export function ChatHeader({
66
65
  {actions}
67
66
  {closeSlot ??
68
67
  (showClose && onClose && (
69
- <TooltipProvider delayDuration={300}>
70
- <Tooltip>
71
- <TooltipTrigger asChild>
72
- <Button
73
- variant="ghost"
74
- size="sm"
75
- onClick={onClose}
76
- aria-label={closeLabel}
77
- className="-mr-1 h-7 w-7 p-0"
78
- >
79
- <X className="h-4 w-4" />
80
- </Button>
81
- </TooltipTrigger>
82
- {/* Tooltip portals to <body> in ui-core's anchored-overlay
83
- tier, already above the dock — no override needed. */}
84
- <TooltipContent side="bottom">{closeLabel}</TooltipContent>
85
- </Tooltip>
86
- </TooltipProvider>
68
+ <Tooltip>
69
+ <TooltipTrigger asChild>
70
+ <Button
71
+ variant="ghost"
72
+ size="sm"
73
+ onClick={onClose}
74
+ aria-label={closeLabel}
75
+ className="-mr-1 h-7 w-7 p-0"
76
+ >
77
+ <X className="h-4 w-4" />
78
+ </Button>
79
+ </TooltipTrigger>
80
+ <TooltipContent side="bottom">{closeLabel}</TooltipContent>
81
+ </Tooltip>
87
82
  ))}
88
83
  </div>
89
84
  </header>
@@ -6,7 +6,6 @@ import type { ButtonHTMLAttributes, ReactNode } from 'react';
6
6
  import {
7
7
  Tooltip,
8
8
  TooltipContent,
9
- TooltipProvider,
10
9
  TooltipTrigger,
11
10
  } from '@djangocfg/ui-core/components';
12
11
  import { cn } from '@djangocfg/ui-core/lib';
@@ -102,19 +101,16 @@ export const ChatHeaderActionButton = forwardRef<HTMLButtonElement, ChatHeaderAc
102
101
  // Opted out — keep the native `title` only.
103
102
  if (tooltip === false) return button;
104
103
 
105
- // Floating tooltip on hover *and* keyboard focus. Self-contained
106
- // `<TooltipProvider>` so the button works standalone without the
107
- // host wiring an ambient provider; Radix dedupes nested providers.
104
+ // Floating tooltip on hover *and* keyboard focus.
105
+ // Host app mounts <UiProviders> (which includes TooltipProvider).
108
106
  const tooltipLabel = tooltip ?? ariaLabel;
109
107
  return (
110
- <TooltipProvider delayDuration={300}>
111
- <Tooltip>
112
- <TooltipTrigger asChild>{button}</TooltipTrigger>
113
- {/* Tooltip portals to <body> in ui-core's anchored-overlay tier,
114
- already above the dock — no z-index override needed. */}
115
- <TooltipContent side="bottom">{tooltipLabel}</TooltipContent>
116
- </Tooltip>
117
- </TooltipProvider>
108
+ <Tooltip>
109
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
110
+ {/* Tooltip portals to <body> in ui-core's anchored-overlay tier,
111
+ already above the dock no z-index override needed. */}
112
+ <TooltipContent side="bottom">{tooltipLabel}</TooltipContent>
113
+ </Tooltip>
118
114
  );
119
115
  },
120
116
  );
@@ -0,0 +1,194 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+
5
+ import { cn } from '@djangocfg/ui-core/lib';
6
+
7
+ /**
8
+ * SuggestedPrompts — chat empty-state "starter prompts" surface.
9
+ *
10
+ * The pattern every chat consumer wants: a small hero (optional), a
11
+ * title + description (optional), and a row/grid of clickable prompts
12
+ * that seed the composer. Pluggable enough to cover ChatGPT-style
13
+ * card grids and cmdop-style flat chips with the same component.
14
+ *
15
+ * Typical wiring inside `<ChatRoot slots={{ empty: ({ setValue, focus }) =>
16
+ * <SuggestedPrompts items={...} onPick={(p) => { setValue(p.prompt); focus(); }} />
17
+ * }} />`.
18
+ */
19
+
20
+ export type SuggestedPromptItem = {
21
+ id: string;
22
+ /** Displayed on the chip / card. */
23
+ label: string;
24
+ /** Sent on click (often equal to `label`). */
25
+ prompt: string;
26
+ /** Optional leading icon. */
27
+ icon?: ReactNode;
28
+ /** Optional secondary line (rendered in `grid` layout, ignored by `chips`). */
29
+ description?: string;
30
+ };
31
+
32
+ export type SuggestedPromptsLayout = 'chips' | 'grid';
33
+
34
+ export interface SuggestedPromptsProps {
35
+ items: readonly SuggestedPromptItem[];
36
+ onPick: (item: SuggestedPromptItem) => void;
37
+ /** Optional title above the chips. */
38
+ title?: ReactNode;
39
+ /** Optional subtitle/description above the chips. */
40
+ description?: ReactNode;
41
+ /** Optional hero element above the title (icon, logo, …). */
42
+ hero?: ReactNode;
43
+ /** Layout — `chips` (flat rounded-full buttons, default) or `grid` (2-col cards). */
44
+ layout?: SuggestedPromptsLayout;
45
+ /** Container className. */
46
+ className?: string;
47
+ /** ARIA label for the region wrapper. Default `Suggested prompts`. */
48
+ ariaLabel?: string;
49
+ /** Custom render override for one item (escape hatch). */
50
+ renderItem?: (item: SuggestedPromptItem, idx: number) => ReactNode;
51
+ }
52
+
53
+ export function SuggestedPrompts({
54
+ items,
55
+ onPick,
56
+ title,
57
+ description,
58
+ hero,
59
+ layout = 'chips',
60
+ className,
61
+ ariaLabel = 'Suggested prompts',
62
+ renderItem,
63
+ }: SuggestedPromptsProps) {
64
+ const hasHeader = hero != null || title != null || description != null;
65
+
66
+ return (
67
+ <section
68
+ role="region"
69
+ aria-label={ariaLabel}
70
+ className={cn(
71
+ 'flex w-full flex-col items-center text-center',
72
+ className,
73
+ )}
74
+ >
75
+ {hasHeader ? (
76
+ <div className="mb-4 flex flex-col items-center gap-1.5">
77
+ {hero != null ? (
78
+ <div className="text-foreground/90">{hero}</div>
79
+ ) : null}
80
+ {title != null ? (
81
+ <h3 className="text-base font-semibold text-foreground">
82
+ {title}
83
+ </h3>
84
+ ) : null}
85
+ {description != null ? (
86
+ <p className="max-w-md text-sm leading-snug text-muted-foreground">
87
+ {description}
88
+ </p>
89
+ ) : null}
90
+ </div>
91
+ ) : null}
92
+
93
+ {layout === 'grid' ? (
94
+ <div
95
+ className="grid w-full max-w-xl grid-cols-1 gap-2 sm:grid-cols-2"
96
+ role="list"
97
+ >
98
+ {items.map((item, idx) =>
99
+ renderItem ? (
100
+ <span key={item.id} role="listitem">
101
+ {renderItem(item, idx)}
102
+ </span>
103
+ ) : (
104
+ <GridCard key={item.id} item={item} onPick={onPick} />
105
+ ),
106
+ )}
107
+ </div>
108
+ ) : (
109
+ <div
110
+ className="flex w-full max-w-xl flex-wrap justify-center gap-2"
111
+ role="list"
112
+ >
113
+ {items.map((item, idx) =>
114
+ renderItem ? (
115
+ <span key={item.id} role="listitem">
116
+ {renderItem(item, idx)}
117
+ </span>
118
+ ) : (
119
+ <Chip key={item.id} item={item} onPick={onPick} />
120
+ ),
121
+ )}
122
+ </div>
123
+ )}
124
+ </section>
125
+ );
126
+ }
127
+
128
+ function Chip({
129
+ item,
130
+ onPick,
131
+ }: {
132
+ item: SuggestedPromptItem;
133
+ onPick: (item: SuggestedPromptItem) => void;
134
+ }) {
135
+ return (
136
+ <button
137
+ type="button"
138
+ role="listitem"
139
+ onClick={() => onPick(item)}
140
+ className={cn(
141
+ 'inline-flex items-center gap-1.5 rounded-full border border-border',
142
+ 'bg-card/60 px-3 py-1.5 text-xs text-muted-foreground',
143
+ 'transition-colors duration-150',
144
+ 'hover:border-primary/40 hover:bg-card hover:text-foreground',
145
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
146
+ 'active:scale-[0.97]',
147
+ )}
148
+ >
149
+ {item.icon != null ? (
150
+ <span aria-hidden className="shrink-0 text-muted-foreground">
151
+ {item.icon}
152
+ </span>
153
+ ) : null}
154
+ <span>{item.label}</span>
155
+ </button>
156
+ );
157
+ }
158
+
159
+ function GridCard({
160
+ item,
161
+ onPick,
162
+ }: {
163
+ item: SuggestedPromptItem;
164
+ onPick: (item: SuggestedPromptItem) => void;
165
+ }) {
166
+ return (
167
+ <button
168
+ type="button"
169
+ role="listitem"
170
+ onClick={() => onPick(item)}
171
+ className={cn(
172
+ 'group flex flex-col gap-1 rounded-md border border-border',
173
+ 'bg-card/60 p-3 text-left',
174
+ 'transition-colors duration-150',
175
+ 'hover:border-primary/40 hover:bg-card',
176
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
177
+ )}
178
+ >
179
+ <span className="flex items-center gap-2 text-sm font-medium text-foreground">
180
+ {item.icon != null ? (
181
+ <span aria-hidden className="shrink-0 text-muted-foreground group-hover:text-foreground">
182
+ {item.icon}
183
+ </span>
184
+ ) : null}
185
+ <span>{item.label}</span>
186
+ </span>
187
+ {item.description ? (
188
+ <span className="text-xs leading-snug text-muted-foreground">
189
+ {item.description}
190
+ </span>
191
+ ) : null}
192
+ </button>
193
+ );
194
+ }
@@ -13,3 +13,9 @@ export {
13
13
  } from './ChatRoot';
14
14
  export { EmptyState, type EmptyStateProps } from './EmptyState';
15
15
  export { ErrorBanner, type ErrorBannerProps } from './ErrorBanner';
16
+ export {
17
+ SuggestedPrompts,
18
+ type SuggestedPromptsProps,
19
+ type SuggestedPromptItem,
20
+ type SuggestedPromptsLayout,
21
+ } from './SuggestedPrompts';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
4
- import type { ListboxRootProps } from './context/ListboxProvider';
4
+ import type { ListboxRootProps } from './types';
5
5
 
6
6
  export const LazyListbox = createLazyComponent<ListboxRootProps>(
7
7
  () => import('./context/ListboxProvider').then((m) => ({ default: m.ListboxRoot })),
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
4
- import type { MasonryProps } from './context/MasonryProvider';
4
+ import type { MasonryProps } from './types';
5
5
 
6
6
  export const LazyMasonry = createLazyComponent<MasonryProps>(
7
7
  () => import('./context/MasonryProvider').then((m) => ({ default: m.Masonry })),
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { createLazyComponent, LoadingFallback } from '../../../components/lazy-wrapper';
4
- import type { TimelineProps } from './context/TimelineProvider';
4
+ import type { TimelineProps } from './types';
5
5
 
6
6
  export const LazyTimeline = createLazyComponent<TimelineProps>(
7
7
  () => import('./context/TimelineProvider').then((m) => ({ default: m.Timeline })),
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { TreeRoot } from './TreeRoot';
4
+ import type { TreeRootProps } from './types';
5
+
6
+ /**
7
+ * `<FinderTree>` — opinionated Finder/Explorer-style preset.
8
+ *
9
+ * Equivalent to `<TreeRoot>` with multi-selection, double-click activation,
10
+ * inline rename, indent guides, and a cozy appearance turned on. Pass an
11
+ * `adapter` to get the built-in CRUD menu wired to `window.dialog.*`.
12
+ *
13
+ * Override any preset default by simply passing the same prop:
14
+ *
15
+ * ```tsx
16
+ * <FinderTree<FsNode>
17
+ * data={data}
18
+ * getItemName={(n) => n.data.name}
19
+ * adapter={fsAdapter}
20
+ * // override one preset default — everything else stays Finder-y:
21
+ * activationMode="single-click-preview"
22
+ * />
23
+ * ```
24
+ */
25
+ export function FinderTree<T>(props: TreeRootProps<T>) {
26
+ return (
27
+ <TreeRoot<T>
28
+ // Finder/Explorer defaults — every one is overridable via `props`.
29
+ selectionMode="multiple"
30
+ activationMode="double-click"
31
+ enableInlineRename
32
+ enableFinderHotkeys
33
+ enableDnD
34
+ enableTypeAhead
35
+ showIndentGuides
36
+ appearance={{ density: 'cozy' }}
37
+ {...props}
38
+ />
39
+ );
40
+ }
41
+
42
+ export default FinderTree;