deepspace 0.3.10 → 0.4.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/cli.js +331 -115
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +199 -16
- package/dist/index.js +354 -166
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +242 -73
- package/dist/server.js +985 -374
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +322 -73
- package/dist/worker.js +961 -343
- package/dist/worker.js.map +1 -1
- package/features/admin-page/feature.json +29 -0
- package/features/admin-page/src/AdminPage.tsx +292 -0
- package/features/admin-page/src/admin-schema.ts +20 -0
- package/features/ai-chat/feature.json +41 -0
- package/features/ai-chat/src/AiChatPage.tsx +692 -0
- package/features/ai-chat/src/ChatPanel.tsx +1098 -0
- package/features/ai-chat/src/ai-chat-schema.ts +11 -0
- package/features/canvas/feature.json +45 -0
- package/features/canvas/src/CanvasEditorPage.tsx +87 -0
- package/features/canvas/src/CanvasListPage.tsx +202 -0
- package/features/canvas/src/canvas-schema.ts +33 -0
- package/features/canvas/src/components/CanvasToolbar.tsx +170 -0
- package/features/canvas/src/components/CanvasView.tsx +369 -0
- package/features/canvas/src/components/ShapeRenderer.tsx +114 -0
- package/features/cron/feature.json +33 -0
- package/features/cron/src/CronLogPage.tsx +100 -0
- package/features/cron/src/cron.ts +25 -0
- package/features/docs/feature.json +96 -0
- package/features/docs/src/DocsEditorPage.tsx +1012 -0
- package/features/docs/src/DocsListPage.tsx +951 -0
- package/features/docs/src/DocsOutlinePanel.tsx +68 -0
- package/features/docs/src/DocsPresence.tsx +138 -0
- package/features/docs/src/InviteDialog.tsx +336 -0
- package/features/docs/src/LibrarySidebar.tsx +464 -0
- package/features/docs/src/docs-favorites.ts +18 -0
- package/features/docs/src/docs-library-types.ts +15 -0
- package/features/docs/src/docs-schema.ts +80 -0
- package/features/docs/src/docs-ui.css +309 -0
- package/features/docs/src/editor/DocEditorSurface.tsx +257 -0
- package/features/docs/src/editor/DocsTiptapToolbar.tsx +560 -0
- package/features/docs/src/editor/useDocEditor.ts +206 -0
- package/features/docs/src/use-yjs-room-with-awareness.ts +285 -0
- package/features/file-attachments/feature.json +42 -0
- package/features/file-attachments/src/AttachmentsPage.tsx +864 -0
- package/features/file-attachments/src/attachments-constants.ts +9 -0
- package/features/file-attachments/src/attachments-schema.ts +45 -0
- package/features/file-attachments/src/useFilePreview.ts +471 -0
- package/features/file-attachments/src/useMimeTypeDetection.ts +138 -0
- package/features/file-manager/feature.json +24 -0
- package/features/file-manager/src/FileManagerPage.tsx +330 -0
- package/features/integration-test/feature.json +18 -0
- package/features/integration-test/src/IntegrationTestPage.tsx +423 -0
- package/features/integration-test/src/integration.spec.ts +42 -0
- package/features/items/feature.json +33 -0
- package/features/items/src/ItemsPage.tsx +325 -0
- package/features/items/src/items-constants.ts +10 -0
- package/features/items/src/items-schema.ts +27 -0
- package/features/kanban/feature.json +18 -0
- package/features/kanban/src/Kanban.tsx +283 -0
- package/features/landing/feature.json +62 -0
- package/features/landing/src/LandingPage.tsx +627 -0
- package/features/landing/src/primitives.tsx +389 -0
- package/features/landing/src/sections/FAQSection.tsx +110 -0
- package/features/landing/src/sections/FeaturesGridSection.tsx +311 -0
- package/features/landing/src/sections/HowItWorksSection.tsx +62 -0
- package/features/landing/src/sections/LogoCloudSection.tsx +58 -0
- package/features/landing/src/sections/ShowcaseSection.tsx +63 -0
- package/features/landing/src/sections/StatsSection.tsx +31 -0
- package/features/landing/src/sections/TeamSection.tsx +86 -0
- package/features/landing/src/sections/TestimonialsSection.tsx +68 -0
- package/features/landing/src/sections/VideoSection.tsx +71 -0
- package/features/leaderboard/feature.json +33 -0
- package/features/leaderboard/src/LeaderboardPage.tsx +372 -0
- package/features/leaderboard/src/leaderboard-constants.ts +21 -0
- package/features/leaderboard/src/leaderboard-schema.ts +36 -0
- package/features/messaging/feature.json +111 -0
- package/features/messaging/src/ChatMultiPage.tsx +300 -0
- package/features/messaging/src/ChatPage.tsx +75 -0
- package/features/messaging/src/components/chat/ChatHeader.tsx +76 -0
- package/features/messaging/src/components/chat/MessageActionSheet.tsx +168 -0
- package/features/messaging/src/components/chat/MessageInput.tsx +65 -0
- package/features/messaging/src/components/chat/MessageItem.tsx +352 -0
- package/features/messaging/src/components/chat/MessageList.tsx +248 -0
- package/features/messaging/src/components/chat/ReactionPicker.tsx +2 -0
- package/features/messaging/src/components/chat/ThreadPanel.tsx +107 -0
- package/features/messaging/src/components/chat/UserProfilePopover.tsx +158 -0
- package/features/messaging/src/components/chat-multi/AddMemberModal.tsx +188 -0
- package/features/messaging/src/components/chat-multi/BrowseGroupsModal.tsx +163 -0
- package/features/messaging/src/components/chat-multi/ChannelListItem.tsx +147 -0
- package/features/messaging/src/components/chat-multi/ChannelSettingsPanel.tsx +344 -0
- package/features/messaging/src/components/chat-multi/ChannelSidebar.tsx +460 -0
- package/features/messaging/src/components/chat-multi/CreateGroupModal.tsx +190 -0
- package/features/messaging/src/components/chat-multi/InvitationList.tsx +149 -0
- package/features/messaging/src/components/chat-multi/NewMessageModal.tsx +159 -0
- package/features/messaging/src/hooks/useChatChannel.ts +64 -0
- package/features/messaging/src/hooks/useLongPress.ts +74 -0
- package/features/messaging/src/hooks/useMultiChannel.ts +142 -0
- package/features/messaging/src/messaging-schema.ts +25 -0
- package/features/presence-test/feature.json +21 -0
- package/features/presence-test/src/PresenceTestPage.tsx +120 -0
- package/features/sidebar/feature.json +19 -0
- package/features/sidebar/src/AppSidebar.tsx +558 -0
- package/features/tasks/feature.json +33 -0
- package/features/tasks/src/TasksPage.tsx +598 -0
- package/features/tasks/src/tasks-constants.ts +43 -0
- package/features/tasks/src/tasks-schema.ts +56 -0
- package/features/testing/feature.json +49 -0
- package/features/testing/src/TestPage.tsx +145 -0
- package/features/testing/src/cpu-burn-action.ts +51 -0
- package/features/testing/src/subrequest-burn-action.ts +53 -0
- package/features/testing/src/test-schema.ts +23 -0
- package/features/topbar/feature.json +18 -0
- package/features/topbar/src/Topbar.tsx +353 -0
- package/features/tree/feature.json +26 -0
- package/features/tree/src/TreeSidebar.tsx +607 -0
- package/features/tree/src/tree-constants.ts +68 -0
- package/features/tree/src/tree-hooks.ts +258 -0
- package/package.json +4 -2
- package/scripts/add-feature.cjs +654 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topbar Component - Horizontal Navigation Bar
|
|
3
|
+
*
|
|
4
|
+
* A top navigation bar with:
|
|
5
|
+
* - Logo/brand area
|
|
6
|
+
* - Horizontal nav links
|
|
7
|
+
* - Search (optional)
|
|
8
|
+
* - User menu
|
|
9
|
+
* - Notification bell
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import React, { useState, useRef, useEffect, type ReactNode } from 'react'
|
|
13
|
+
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Types
|
|
16
|
+
// ============================================================================
|
|
17
|
+
|
|
18
|
+
export interface NavItem {
|
|
19
|
+
id: string
|
|
20
|
+
label: string
|
|
21
|
+
path?: string
|
|
22
|
+
icon?: ReactNode
|
|
23
|
+
badge?: number | string
|
|
24
|
+
onClick?: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface TopbarProps {
|
|
28
|
+
/** Logo or brand element */
|
|
29
|
+
logo?: ReactNode
|
|
30
|
+
/** Navigation items */
|
|
31
|
+
navItems?: NavItem[]
|
|
32
|
+
/** Currently active item ID */
|
|
33
|
+
activeId?: string
|
|
34
|
+
/** Callback when nav item clicked */
|
|
35
|
+
onNavigate?: (item: NavItem) => void
|
|
36
|
+
/** Show search input */
|
|
37
|
+
showSearch?: boolean
|
|
38
|
+
/** Search placeholder */
|
|
39
|
+
searchPlaceholder?: string
|
|
40
|
+
/** Search callback */
|
|
41
|
+
onSearch?: (query: string) => void
|
|
42
|
+
/** Notification count */
|
|
43
|
+
notificationCount?: number
|
|
44
|
+
/** Notification click handler */
|
|
45
|
+
onNotificationClick?: () => void
|
|
46
|
+
/** User info */
|
|
47
|
+
user?: {
|
|
48
|
+
name?: string
|
|
49
|
+
email?: string
|
|
50
|
+
imageUrl?: string
|
|
51
|
+
}
|
|
52
|
+
/** User menu items */
|
|
53
|
+
userMenuItems?: Array<{
|
|
54
|
+
id: string
|
|
55
|
+
label: string
|
|
56
|
+
icon?: ReactNode
|
|
57
|
+
onClick?: () => void
|
|
58
|
+
danger?: boolean
|
|
59
|
+
}>
|
|
60
|
+
/** Custom right-side content */
|
|
61
|
+
rightContent?: ReactNode
|
|
62
|
+
/** Sticky positioning */
|
|
63
|
+
sticky?: boolean
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Icons
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
function SearchIcon({ className = '' }: { className?: string }) {
|
|
71
|
+
return (
|
|
72
|
+
<svg className={className} width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
73
|
+
<circle cx="11" cy="11" r="8" />
|
|
74
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
75
|
+
</svg>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function BellIcon({ className = '' }: { className?: string }) {
|
|
80
|
+
return (
|
|
81
|
+
<svg className={className} width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
82
|
+
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" />
|
|
83
|
+
<path d="M13.73 21a2 2 0 0 1-3.46 0" />
|
|
84
|
+
</svg>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function ChevronDownIcon({ className = '' }: { className?: string }) {
|
|
89
|
+
return (
|
|
90
|
+
<svg className={className} width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
91
|
+
<polyline points="6 9 12 15 18 9" />
|
|
92
|
+
</svg>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ============================================================================
|
|
97
|
+
// Dropdown Menu
|
|
98
|
+
// ============================================================================
|
|
99
|
+
|
|
100
|
+
interface DropdownProps {
|
|
101
|
+
trigger: ReactNode
|
|
102
|
+
children: ReactNode
|
|
103
|
+
align?: 'left' | 'right'
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function Dropdown({ trigger, children, align = 'right' }: DropdownProps) {
|
|
107
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
108
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
function handleClickOutside(event: MouseEvent) {
|
|
112
|
+
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
113
|
+
setIsOpen(false)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
117
|
+
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
118
|
+
}, [])
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div ref={ref} className="relative">
|
|
122
|
+
<div onClick={() => setIsOpen(!isOpen)}>{trigger}</div>
|
|
123
|
+
{isOpen && (
|
|
124
|
+
<div
|
|
125
|
+
className={`
|
|
126
|
+
absolute top-full mt-2 min-w-[200px] bg-card rounded-lg border border-border
|
|
127
|
+
shadow-lg py-1 z-50
|
|
128
|
+
${align === 'right' ? 'right-0' : 'left-0'}
|
|
129
|
+
`}
|
|
130
|
+
>
|
|
131
|
+
{children}
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Topbar Component
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
export default function Topbar({
|
|
143
|
+
logo,
|
|
144
|
+
navItems = [],
|
|
145
|
+
activeId,
|
|
146
|
+
onNavigate,
|
|
147
|
+
showSearch = false,
|
|
148
|
+
searchPlaceholder = 'Search...',
|
|
149
|
+
onSearch,
|
|
150
|
+
notificationCount,
|
|
151
|
+
onNotificationClick,
|
|
152
|
+
user,
|
|
153
|
+
userMenuItems = [],
|
|
154
|
+
rightContent,
|
|
155
|
+
sticky = true,
|
|
156
|
+
}: TopbarProps) {
|
|
157
|
+
const [searchQuery, setSearchQuery] = useState('')
|
|
158
|
+
|
|
159
|
+
const handleSearch = (e: React.FormEvent) => {
|
|
160
|
+
e.preventDefault()
|
|
161
|
+
onSearch?.(searchQuery)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<header
|
|
166
|
+
className={`
|
|
167
|
+
bg-card/80 backdrop-blur-xl border-b border-border z-40
|
|
168
|
+
${sticky ? 'sticky top-0' : ''}
|
|
169
|
+
`}
|
|
170
|
+
>
|
|
171
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
172
|
+
<div className="flex items-center justify-between h-16">
|
|
173
|
+
{/* Left: Logo + Nav */}
|
|
174
|
+
<div className="flex items-center gap-8">
|
|
175
|
+
{logo && (
|
|
176
|
+
<div className="flex-shrink-0">
|
|
177
|
+
{logo}
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
|
|
181
|
+
{navItems.length > 0 && (
|
|
182
|
+
<nav className="hidden md:flex items-center gap-1">
|
|
183
|
+
{navItems.map((item) => {
|
|
184
|
+
const isActive = activeId === item.id
|
|
185
|
+
return (
|
|
186
|
+
<button
|
|
187
|
+
key={item.id}
|
|
188
|
+
onClick={() => {
|
|
189
|
+
item.onClick?.()
|
|
190
|
+
onNavigate?.(item)
|
|
191
|
+
}}
|
|
192
|
+
className={`
|
|
193
|
+
flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors
|
|
194
|
+
${isActive
|
|
195
|
+
? 'bg-primary/10 text-primary'
|
|
196
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted'
|
|
197
|
+
}
|
|
198
|
+
`}
|
|
199
|
+
>
|
|
200
|
+
{item.icon}
|
|
201
|
+
<span>{item.label}</span>
|
|
202
|
+
{item.badge !== undefined && (
|
|
203
|
+
<span className={`
|
|
204
|
+
text-xs px-1.5 py-0.5 rounded-full
|
|
205
|
+
${isActive ? 'bg-primary/20 text-primary' : 'bg-muted text-muted-foreground'}
|
|
206
|
+
`}>
|
|
207
|
+
{item.badge}
|
|
208
|
+
</span>
|
|
209
|
+
)}
|
|
210
|
+
</button>
|
|
211
|
+
)
|
|
212
|
+
})}
|
|
213
|
+
</nav>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{/* Right: Search, Notifications, User */}
|
|
218
|
+
<div className="flex items-center gap-3">
|
|
219
|
+
{showSearch && (
|
|
220
|
+
<form onSubmit={handleSearch} className="hidden sm:block">
|
|
221
|
+
<div className="relative">
|
|
222
|
+
<SearchIcon className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
|
223
|
+
<input
|
|
224
|
+
type="text"
|
|
225
|
+
value={searchQuery}
|
|
226
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
227
|
+
placeholder={searchPlaceholder}
|
|
228
|
+
className="
|
|
229
|
+
w-64 pl-10 pr-4 py-2 text-sm
|
|
230
|
+
bg-muted border border-border rounded-lg
|
|
231
|
+
text-foreground placeholder:text-muted-foreground
|
|
232
|
+
focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary
|
|
233
|
+
"
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
</form>
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{rightContent}
|
|
240
|
+
|
|
241
|
+
{onNotificationClick && (
|
|
242
|
+
<button
|
|
243
|
+
onClick={onNotificationClick}
|
|
244
|
+
className="relative p-2 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
245
|
+
>
|
|
246
|
+
<BellIcon />
|
|
247
|
+
{notificationCount !== undefined && notificationCount > 0 && (
|
|
248
|
+
<span className="absolute -top-0.5 -right-0.5 min-w-[18px] h-[18px] flex items-center justify-center text-[10px] font-bold text-destructive-foreground bg-destructive rounded-full px-1">
|
|
249
|
+
{notificationCount > 99 ? '99+' : notificationCount}
|
|
250
|
+
</span>
|
|
251
|
+
)}
|
|
252
|
+
</button>
|
|
253
|
+
)}
|
|
254
|
+
|
|
255
|
+
{user && (
|
|
256
|
+
<Dropdown
|
|
257
|
+
trigger={
|
|
258
|
+
<button className="flex items-center gap-2 p-1.5 rounded-lg hover:bg-muted transition-colors">
|
|
259
|
+
{user.imageUrl ? (
|
|
260
|
+
<img src={user.imageUrl} alt="" className="w-8 h-8 rounded-full" />
|
|
261
|
+
) : (
|
|
262
|
+
<div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center">
|
|
263
|
+
<span className="text-sm font-medium text-primary">
|
|
264
|
+
{user.name?.[0]?.toUpperCase() || user.email?.[0]?.toUpperCase() || '?'}
|
|
265
|
+
</span>
|
|
266
|
+
</div>
|
|
267
|
+
)}
|
|
268
|
+
<span className="hidden lg:block text-sm text-muted-foreground">
|
|
269
|
+
{user.name || user.email}
|
|
270
|
+
</span>
|
|
271
|
+
<ChevronDownIcon className="text-muted-foreground" />
|
|
272
|
+
</button>
|
|
273
|
+
}
|
|
274
|
+
>
|
|
275
|
+
<div className="px-4 py-3 border-b border-border">
|
|
276
|
+
<div className="text-sm font-medium text-foreground">{user.name}</div>
|
|
277
|
+
{user.email && (
|
|
278
|
+
<div className="text-xs text-muted-foreground truncate">{user.email}</div>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<div className="py-1">
|
|
283
|
+
{userMenuItems.map((item) => (
|
|
284
|
+
<button
|
|
285
|
+
key={item.id}
|
|
286
|
+
onClick={item.onClick}
|
|
287
|
+
className={`
|
|
288
|
+
w-full flex items-center gap-3 px-4 py-2 text-sm text-left transition-colors
|
|
289
|
+
${item.danger
|
|
290
|
+
? 'text-destructive hover:bg-destructive/10'
|
|
291
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
292
|
+
}
|
|
293
|
+
`}
|
|
294
|
+
>
|
|
295
|
+
{item.icon}
|
|
296
|
+
<span>{item.label}</span>
|
|
297
|
+
</button>
|
|
298
|
+
))}
|
|
299
|
+
</div>
|
|
300
|
+
</Dropdown>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
{navItems.length > 0 && (
|
|
307
|
+
<div className="md:hidden border-t border-border px-4 py-2 overflow-x-auto">
|
|
308
|
+
<div className="flex gap-1">
|
|
309
|
+
{navItems.map((item) => {
|
|
310
|
+
const isActive = activeId === item.id
|
|
311
|
+
return (
|
|
312
|
+
<button
|
|
313
|
+
key={item.id}
|
|
314
|
+
onClick={() => {
|
|
315
|
+
item.onClick?.()
|
|
316
|
+
onNavigate?.(item)
|
|
317
|
+
}}
|
|
318
|
+
className={`
|
|
319
|
+
flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm whitespace-nowrap transition-colors
|
|
320
|
+
${isActive
|
|
321
|
+
? 'bg-primary/10 text-primary font-medium'
|
|
322
|
+
: 'text-muted-foreground'
|
|
323
|
+
}
|
|
324
|
+
`}
|
|
325
|
+
>
|
|
326
|
+
{item.icon}
|
|
327
|
+
<span>{item.label}</span>
|
|
328
|
+
</button>
|
|
329
|
+
)
|
|
330
|
+
})}
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
</header>
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ============================================================================
|
|
339
|
+
// Example Logo Component
|
|
340
|
+
// ============================================================================
|
|
341
|
+
|
|
342
|
+
export function ExampleLogo({ collapsed = false }: { collapsed?: boolean }) {
|
|
343
|
+
return (
|
|
344
|
+
<div className="flex items-center gap-2">
|
|
345
|
+
<div className="w-8 h-8 bg-primary/20 rounded-lg flex items-center justify-center">
|
|
346
|
+
<svg className="w-5 h-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
347
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
|
348
|
+
</svg>
|
|
349
|
+
</div>
|
|
350
|
+
{!collapsed && <span className="text-lg font-semibold text-foreground">My App</span>}
|
|
351
|
+
</div>
|
|
352
|
+
)
|
|
353
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "tree",
|
|
3
|
+
"name": "Tree Sidebar",
|
|
4
|
+
"category": "nav",
|
|
5
|
+
"description": "Hierarchical tree navigation with drag-and-drop reordering",
|
|
6
|
+
"details": "Tree sidebar with main navigation views (All, Today, Upcoming, Logbook, Trash), hierarchical project tree with drag-and-drop reordering, user list with task assignment via drag-and-drop, and collapsible sections.",
|
|
7
|
+
"files": [
|
|
8
|
+
{
|
|
9
|
+
"src": "src/TreeSidebar.tsx",
|
|
10
|
+
"dest": "src/components/tree/TreeSidebar.tsx"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"src": "src/tree-constants.ts",
|
|
14
|
+
"dest": "src/components/tree/tree-constants.ts"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"src": "src/tree-hooks.ts",
|
|
18
|
+
"dest": "src/components/tree/tree-hooks.ts"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"requires": [],
|
|
22
|
+
"instructions": [
|
|
23
|
+
"Configure project nodes and user list to populate the tree"
|
|
24
|
+
],
|
|
25
|
+
"patterns": []
|
|
26
|
+
}
|