@geenius/tools 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/.changeset/config.json +11 -0
- package/.env.example +2 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
- package/.github/dependabot.yml +11 -0
- package/.github/workflows/ci.yml +23 -0
- package/.github/workflows/release.yml +29 -0
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/.prettierrc +7 -0
- package/.project/ACCOUNT.yaml +4 -0
- package/.project/IDEAS.yaml +7 -0
- package/.project/PROJECT.yaml +11 -0
- package/.project/ROADMAP.yaml +15 -0
- package/CHANGELOG.md +16 -0
- package/CODE_OF_CONDUCT.md +26 -0
- package/CONTRIBUTING.md +69 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/SECURITY.md +18 -0
- package/SUPPORT.md +14 -0
- package/package.json +75 -0
- package/packages/convex/shared/README.md +1 -0
- package/packages/convex/shared/package.json +42 -0
- package/packages/convex/shared/src/audit/index.ts +5 -0
- package/packages/convex/shared/src/audit/presets.ts +165 -0
- package/packages/convex/shared/src/audit/schema.ts +85 -0
- package/packages/convex/shared/src/audit/write.ts +102 -0
- package/packages/convex/shared/src/extract.ts +75 -0
- package/packages/convex/shared/src/index.ts +41 -0
- package/packages/convex/shared/src/messages.ts +45 -0
- package/packages/convex/shared/src/security.ts +112 -0
- package/packages/convex/shared/src/throw.ts +184 -0
- package/packages/convex/shared/src/types.ts +57 -0
- package/packages/convex/shared/src/utils.ts +58 -0
- package/packages/convex/shared/tsconfig.json +28 -0
- package/packages/convex/shared/tsup.config.ts +12 -0
- package/packages/devtools/package.json +27 -0
- package/packages/devtools/react/README.md +1 -0
- package/packages/devtools/react/package.json +53 -0
- package/packages/devtools/react/src/components/DesignPreview.tsx +59 -0
- package/packages/devtools/react/src/components/DesignSwitcherDropdown.tsx +99 -0
- package/packages/devtools/react/src/components/DevSidebar.tsx +247 -0
- package/packages/devtools/react/src/components/DevToolbar.tsx +242 -0
- package/packages/devtools/react/src/components/GitHubIssueDialog.tsx +402 -0
- package/packages/devtools/react/src/components/InspectorOverlay.tsx +312 -0
- package/packages/devtools/react/src/components/PageLoadWaterfall.tsx +144 -0
- package/packages/devtools/react/src/components/PerformancePanel.tsx +330 -0
- package/packages/devtools/react/src/context/DevModeContext.tsx +226 -0
- package/packages/devtools/react/src/context/PerformanceContext.tsx +143 -0
- package/packages/devtools/react/src/data/designs.ts +13 -0
- package/packages/devtools/react/src/hooks/useGitHubLabels.ts +47 -0
- package/packages/devtools/react/src/hooks/useVirtualList.ts +124 -0
- package/packages/devtools/react/src/index.ts +77 -0
- package/packages/devtools/react/src/panels/ConvexSpy.tsx +130 -0
- package/packages/devtools/react/src/panels/DatabaseSeeder.tsx +116 -0
- package/packages/devtools/react/src/panels/DevModePhase2.tsx +191 -0
- package/packages/devtools/react/src/panels/DevModePhase3.tsx +234 -0
- package/packages/devtools/react/src/panels/FeatureFlagsToggle.tsx +104 -0
- package/packages/devtools/react/src/panels/QuickRouteJump.tsx +152 -0
- package/packages/devtools/react/src/services/github-service.ts +247 -0
- package/packages/devtools/react/tsconfig.json +31 -0
- package/packages/devtools/react/tsup.config.ts +18 -0
- package/packages/devtools/solidjs/README.md +1 -0
- package/packages/devtools/solidjs/package.json +49 -0
- package/packages/devtools/solidjs/src/components/DesignPreview.tsx +51 -0
- package/packages/devtools/solidjs/src/components/DesignSwitcherDropdown.tsx +95 -0
- package/packages/devtools/solidjs/src/components/DevSidebar.tsx +247 -0
- package/packages/devtools/solidjs/src/components/DevToolbar.tsx +242 -0
- package/packages/devtools/solidjs/src/components/GitHubIssueDialog.tsx +400 -0
- package/packages/devtools/solidjs/src/components/InspectorOverlay.tsx +311 -0
- package/packages/devtools/solidjs/src/components/PageLoadWaterfall.tsx +144 -0
- package/packages/devtools/solidjs/src/components/PerformancePanel.tsx +330 -0
- package/packages/devtools/solidjs/src/context/DevModeContext.tsx +216 -0
- package/packages/devtools/solidjs/src/context/PerformanceContext.tsx +135 -0
- package/packages/devtools/solidjs/src/data/designs.ts +13 -0
- package/packages/devtools/solidjs/src/hooks/createGitHubLabels.ts +47 -0
- package/packages/devtools/solidjs/src/index.ts +64 -0
- package/packages/devtools/solidjs/src/services/github-service.ts +247 -0
- package/packages/devtools/solidjs/tsconfig.json +21 -0
- package/packages/devtools/src/index.ts +377 -0
- package/packages/devtools/tsup.config.ts +12 -0
- package/packages/env/package.json +30 -0
- package/packages/env/src/index.ts +264 -0
- package/packages/env/tsup.config.ts +12 -0
- package/packages/errors/package.json +27 -0
- package/packages/errors/react/README.md +1 -0
- package/packages/errors/react/package.json +72 -0
- package/packages/errors/react/src/analytics.ts +16 -0
- package/packages/errors/react/src/components/ErrorBoundary.tsx +248 -0
- package/packages/errors/react/src/components/ErrorDisplay.tsx +328 -0
- package/packages/errors/react/src/components/ValidationErrors.tsx +102 -0
- package/packages/errors/react/src/config.ts +199 -0
- package/packages/errors/react/src/constants.ts +74 -0
- package/packages/errors/react/src/hooks/useErrorBoundary.ts +92 -0
- package/packages/errors/react/src/hooks/useErrorHandler.ts +87 -0
- package/packages/errors/react/src/index.ts +96 -0
- package/packages/errors/react/src/types.ts +102 -0
- package/packages/errors/react/src/utils/errorMessages.ts +35 -0
- package/packages/errors/react/src/utils/errorPolicy.ts +139 -0
- package/packages/errors/react/src/utils/extractAppError.ts +174 -0
- package/packages/errors/react/src/utils/formatError.ts +112 -0
- package/packages/errors/react/tsconfig.json +25 -0
- package/packages/errors/react/tsup.config.ts +24 -0
- package/packages/errors/solidjs/README.md +1 -0
- package/packages/errors/solidjs/package.json +46 -0
- package/packages/errors/solidjs/src/components/ErrorDisplay.tsx +179 -0
- package/packages/errors/solidjs/src/config.ts +98 -0
- package/packages/errors/solidjs/src/hooks/createErrorHandler.ts +107 -0
- package/packages/errors/solidjs/src/index.ts +61 -0
- package/packages/errors/solidjs/src/types.ts +34 -0
- package/packages/errors/solidjs/src/utils/errorPolicy.ts +56 -0
- package/packages/errors/solidjs/src/utils/extractAppError.ts +94 -0
- package/packages/errors/solidjs/src/utils/formatError.ts +33 -0
- package/packages/errors/solidjs/tsconfig.json +26 -0
- package/packages/errors/solidjs/tsup.config.ts +21 -0
- package/packages/errors/src/index.ts +320 -0
- package/packages/errors/tsup.config.ts +12 -0
- package/packages/logger/package.json +27 -0
- package/packages/logger/react/README.md +1 -0
- package/packages/logger/react/package.json +46 -0
- package/packages/logger/react/src/index.ts +4 -0
- package/packages/logger/react/src/useMetrics.ts +42 -0
- package/packages/logger/react/src/usePerformanceLog.ts +61 -0
- package/packages/logger/react/tsconfig.json +31 -0
- package/packages/logger/react/tsup.config.ts +12 -0
- package/packages/logger/solidjs/README.md +1 -0
- package/packages/logger/solidjs/package.json +45 -0
- package/packages/logger/solidjs/src/createMetrics.ts +37 -0
- package/packages/logger/solidjs/src/createPerformanceLog.ts +58 -0
- package/packages/logger/solidjs/src/index.ts +4 -0
- package/packages/logger/solidjs/tsconfig.json +32 -0
- package/packages/logger/solidjs/tsup.config.ts +12 -0
- package/packages/logger/src/index.ts +363 -0
- package/packages/logger/tsup.config.ts +12 -0
- package/packages/perf/package.json +27 -0
- package/packages/perf/react/README.md +1 -0
- package/packages/perf/react/package.json +59 -0
- package/packages/perf/react/src/components/PerformanceDashboard.tsx +257 -0
- package/packages/perf/react/src/hooks/useMonitoredQuery.ts +89 -0
- package/packages/perf/react/src/hooks/usePerformanceMetrics.ts +78 -0
- package/packages/perf/react/src/index.ts +33 -0
- package/packages/perf/react/src/services/PerformanceMonitor.ts +313 -0
- package/packages/perf/react/src/types.ts +77 -0
- package/packages/perf/react/tsconfig.json +25 -0
- package/packages/perf/react/tsup.config.ts +19 -0
- package/packages/perf/solidjs/README.md +1 -0
- package/packages/perf/solidjs/package.json +41 -0
- package/packages/perf/solidjs/src/components/PerformanceDashboard.tsx +207 -0
- package/packages/perf/solidjs/src/hooks/createPerformanceMetrics.ts +73 -0
- package/packages/perf/solidjs/src/index.ts +31 -0
- package/packages/perf/solidjs/src/services/PerformanceMonitor.ts +134 -0
- package/packages/perf/solidjs/src/types.ts +78 -0
- package/packages/perf/solidjs/tsconfig.json +26 -0
- package/packages/perf/solidjs/tsup.config.ts +14 -0
- package/packages/perf/src/index.ts +410 -0
- package/packages/perf/tsup.config.ts +12 -0
- package/pnpm-workspace.yaml +2 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// @geenius-tools/devtools-solidjs — src/components/DevSidebar.tsx
|
|
2
|
+
|
|
3
|
+
import { createSignal, createEffect, createMemo } from 'solid-js'
|
|
4
|
+
import { cn } from '@geenius-ui/solid'
|
|
5
|
+
import {
|
|
6
|
+
Bug,
|
|
7
|
+
Lightbulb,
|
|
8
|
+
ExternalLink,
|
|
9
|
+
Clock,
|
|
10
|
+
X,
|
|
11
|
+
RefreshCw,
|
|
12
|
+
Maximize2,
|
|
13
|
+
Minimize2,
|
|
14
|
+
} from 'lucide-solid'
|
|
15
|
+
import { createDevModeOptional } from '../context/DevModeContext'
|
|
16
|
+
import {
|
|
17
|
+
getGitHubIssues,
|
|
18
|
+
getGitHubConfig,
|
|
19
|
+
getIssueType,
|
|
20
|
+
formatRelativeTime,
|
|
21
|
+
type GitHubIssueListItem,
|
|
22
|
+
} from '../services/github-service'
|
|
23
|
+
|
|
24
|
+
interface IssueCardProps {
|
|
25
|
+
issue: GitHubIssueListItem
|
|
26
|
+
expandedView: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const IssueCard = memo(function IssueCard({
|
|
30
|
+
issue,
|
|
31
|
+
expandedView,
|
|
32
|
+
}: IssueCardProps) {
|
|
33
|
+
const issueType = getIssueType(issue)
|
|
34
|
+
|
|
35
|
+
const linkedComponent = createMemo(() => {
|
|
36
|
+
if (!issue.body) return null
|
|
37
|
+
const match = issue.body.match(/\*\*Name:\*\*\s*`([^`]+)`/)
|
|
38
|
+
return match ? match[1] : null
|
|
39
|
+
}, [issue.body])
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<a
|
|
43
|
+
href={issue.html_url}
|
|
44
|
+
target="_blank"
|
|
45
|
+
rel="noopener noreferrer"
|
|
46
|
+
class={cn(
|
|
47
|
+
'block p-3 rounded-lg border transition-all duration-200',
|
|
48
|
+
'hover:bg-white/5 hover:border-white/20',
|
|
49
|
+
'border-white/10 bg-white/[0.02]',
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
<div class="flex items-start gap-3">
|
|
53
|
+
<div
|
|
54
|
+
class={cn(
|
|
55
|
+
'flex-shrink-0 w-8 h-8 rounded-lg flex items-center justify-center',
|
|
56
|
+
issueType === 'bug' && 'bg-red-500/10 text-red-500',
|
|
57
|
+
issueType === 'feature' && 'bg-emerald-500/10 text-emerald-500',
|
|
58
|
+
issueType === 'other' && 'bg-blue-500/10 text-blue-500',
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
{issueType === 'bug' ? (
|
|
62
|
+
<Bug class="w-4 h-4" />
|
|
63
|
+
) : (
|
|
64
|
+
<Lightbulb class="w-4 h-4" />
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div class="flex-1 min-w-0">
|
|
69
|
+
<p class="font-medium text-sm text-white line-clamp-2">
|
|
70
|
+
{issue.title}
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
{linkedComponent && (
|
|
74
|
+
<span class="inline-flex items-center gap-1 mt-1 text-[10px] px-1.5 py-0.5 bg-white/5 border border-white/10 rounded text-white/60">
|
|
75
|
+
{linkedComponent}
|
|
76
|
+
</span>
|
|
77
|
+
)}
|
|
78
|
+
|
|
79
|
+
<div class="flex items-center gap-3 mt-2 text-xs text-white/40">
|
|
80
|
+
<span>#{issue.number}</span>
|
|
81
|
+
<span class="flex items-center gap-1">
|
|
82
|
+
<Clock class="w-3 h-3" />
|
|
83
|
+
{formatRelativeTime(issue.updated_at)}
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
{expandedView && issue.body && (
|
|
88
|
+
<div class="mt-3 text-xs text-white/40 border-l-2 border-white/10 pl-2 line-clamp-6 whitespace-pre-line font-mono bg-white/[0.02] p-2 rounded-sm">
|
|
89
|
+
{issue.body.substring(0, 300)}
|
|
90
|
+
{issue.body.length > 300 ? '...' : ''}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
<ExternalLink class="w-4 h-4 text-white/30 flex-shrink-0" />
|
|
95
|
+
</div>
|
|
96
|
+
</a>
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
export const DevSidebar = memo(function DevSidebar() {
|
|
101
|
+
const devMode = createDevModeOptional()
|
|
102
|
+
const [issues, setIssues] = createSignal<GitHubIssueListItem[]>([])
|
|
103
|
+
const [loading, setLoading] = createSignal(false)
|
|
104
|
+
const [tab, setTab] = createSignal<'bugs' | 'features'>('bugs')
|
|
105
|
+
const [isExpanded, setIsExpanded] = createSignal(false)
|
|
106
|
+
|
|
107
|
+
const isSidebarOpen = devMode?.isSidebarOpen ?? false
|
|
108
|
+
const setSidebarOpen = devMode?.setSidebarOpen ?? (() => { })
|
|
109
|
+
|
|
110
|
+
createEffect(() => {
|
|
111
|
+
if (isSidebarOpen && issues.length === 0 && !loading) {
|
|
112
|
+
fetchIssues()
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const fetchIssues = async () => {
|
|
117
|
+
setLoading(true)
|
|
118
|
+
try {
|
|
119
|
+
const result = await getGitHubIssues('all', 'open')
|
|
120
|
+
setIssues(result)
|
|
121
|
+
} catch {
|
|
122
|
+
// Silently fail
|
|
123
|
+
} finally {
|
|
124
|
+
setLoading(false)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const handleRefresh = () => fetchIssues()
|
|
129
|
+
const toggleExpand = () => setIsExpanded((prev) => !prev)
|
|
130
|
+
|
|
131
|
+
const { bugs, features } = createMemo(() => {
|
|
132
|
+
const bugs: GitHubIssueListItem[] = []
|
|
133
|
+
const features: GitHubIssueListItem[] = []
|
|
134
|
+
|
|
135
|
+
issues.forEach((issue) => {
|
|
136
|
+
const type = getIssueType(issue)
|
|
137
|
+
if (type === 'bug') bugs.push(issue)
|
|
138
|
+
else features.push(issue)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return { bugs, features }
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const currentIssues = tab === 'bugs' ? bugs : features
|
|
145
|
+
|
|
146
|
+
if (!devMode || !isSidebarOpen) return null
|
|
147
|
+
|
|
148
|
+
const config = getGitHubConfig()
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<div
|
|
152
|
+
data-dev-tool="true"
|
|
153
|
+
class={cn(
|
|
154
|
+
'fixed top-0 right-0 h-full z-50',
|
|
155
|
+
'flex flex-col',
|
|
156
|
+
'bg-gradient-to-b from-slate-900/98 to-slate-950/98',
|
|
157
|
+
'backdrop-blur-xl border-l border-white/10',
|
|
158
|
+
'shadow-2xl shadow-black/50',
|
|
159
|
+
'transition-all duration-300 ease-in-out',
|
|
160
|
+
isExpanded ? 'w-full sm:max-w-[50vw]' : 'w-full sm:max-w-md',
|
|
161
|
+
'animate-in slide-in-from-right duration-300',
|
|
162
|
+
)}
|
|
163
|
+
>
|
|
164
|
+
{/* Header */}
|
|
165
|
+
<div class="flex items-center justify-between px-4 py-3 border-b border-white/10">
|
|
166
|
+
<div class="flex items-center gap-2">
|
|
167
|
+
<Bug class="w-4 h-4 text-primary" />
|
|
168
|
+
<span class="text-sm font-bold text-white">GitHub Issues</span>
|
|
169
|
+
<span class="text-xs text-white/40">
|
|
170
|
+
({config.owner}/{config.repo})
|
|
171
|
+
</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="flex items-center gap-1">
|
|
174
|
+
<button
|
|
175
|
+
onClick={handleRefresh}
|
|
176
|
+
disabled={loading}
|
|
177
|
+
class={cn(
|
|
178
|
+
'p-1.5 text-white/40 hover:text-white hover:bg-white/10 rounded-lg transition-colors',
|
|
179
|
+
loading && 'animate-spin',
|
|
180
|
+
)}
|
|
181
|
+
>
|
|
182
|
+
<RefreshCw class="w-3.5 h-3.5" />
|
|
183
|
+
</button>
|
|
184
|
+
<button
|
|
185
|
+
onClick={toggleExpand}
|
|
186
|
+
class="hidden sm:block p-1.5 text-white/40 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
|
187
|
+
>
|
|
188
|
+
{isExpanded ? (
|
|
189
|
+
<Minimize2 class="w-3.5 h-3.5" />
|
|
190
|
+
) : (
|
|
191
|
+
<Maximize2 class="w-3.5 h-3.5" />
|
|
192
|
+
)}
|
|
193
|
+
</button>
|
|
194
|
+
<button
|
|
195
|
+
onClick={() => setSidebarOpen(false)}
|
|
196
|
+
class="p-1.5 text-white/40 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
|
|
197
|
+
>
|
|
198
|
+
<X class="w-4 h-4" />
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
{/* Tabs */}
|
|
204
|
+
<div class="flex border-b border-white/10">
|
|
205
|
+
<button
|
|
206
|
+
onClick={() => setTab('bugs')}
|
|
207
|
+
class={cn(
|
|
208
|
+
'flex-1 px-4 py-2.5 text-xs font-medium transition-colors',
|
|
209
|
+
tab === 'bugs'
|
|
210
|
+
? 'text-red-400 border-b-2 border-red-500'
|
|
211
|
+
: 'text-white/40 hover:text-white/70',
|
|
212
|
+
)}
|
|
213
|
+
>
|
|
214
|
+
🐛 Bugs ({bugs.length})
|
|
215
|
+
</button>
|
|
216
|
+
<button
|
|
217
|
+
onClick={() => setTab('features')}
|
|
218
|
+
class={cn(
|
|
219
|
+
'flex-1 px-4 py-2.5 text-xs font-medium transition-colors',
|
|
220
|
+
tab === 'features'
|
|
221
|
+
? 'text-emerald-400 border-b-2 border-emerald-500'
|
|
222
|
+
: 'text-white/40 hover:text-white/70',
|
|
223
|
+
)}
|
|
224
|
+
>
|
|
225
|
+
✨ Features ({features.length})
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
{/* Content */}
|
|
230
|
+
<div class="flex-1 overflow-y-auto p-3 space-y-2">
|
|
231
|
+
{loading ? (
|
|
232
|
+
<div class="flex items-center justify-center py-12">
|
|
233
|
+
<div class="w-6 h-6 border-2 border-white/20 border-t-primary rounded-full animate-spin" />
|
|
234
|
+
</div>
|
|
235
|
+
) : currentIssues.length === 0 ? (
|
|
236
|
+
<div class="text-center py-12 text-sm text-white/30">
|
|
237
|
+
No {tab === 'bugs' ? 'bugs' : 'features'} found
|
|
238
|
+
</div>
|
|
239
|
+
) : (
|
|
240
|
+
currentIssues.map((issue) => (
|
|
241
|
+
<IssueCard key={issue.id} issue={issue} expandedView={isExpanded} />
|
|
242
|
+
))
|
|
243
|
+
)}
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
)
|
|
247
|
+
})
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// @geenius-tools/devtools-solidjs — src/components/DevToolbar.tsx
|
|
2
|
+
|
|
3
|
+
import { createSignal, createEffect } from 'solid-js'
|
|
4
|
+
import { cn } from '@geenius-ui/solid'
|
|
5
|
+
import {
|
|
6
|
+
Power,
|
|
7
|
+
PanelRight,
|
|
8
|
+
Bug,
|
|
9
|
+
Lightbulb,
|
|
10
|
+
Target,
|
|
11
|
+
Activity,
|
|
12
|
+
} from 'lucide-solid'
|
|
13
|
+
import { createDevModeOptional, createIsDevAllowed } from '../context/DevModeContext'
|
|
14
|
+
import { createPerformanceContextOptional } from '../context/PerformanceContext'
|
|
15
|
+
import { GitHubIssueDialog } from './GitHubIssueDialog'
|
|
16
|
+
import { PerformancePanel } from './PerformancePanel'
|
|
17
|
+
|
|
18
|
+
interface ToolbarButtonProps {
|
|
19
|
+
icon: React.JSX.Element
|
|
20
|
+
label: string
|
|
21
|
+
onClick?: () => void
|
|
22
|
+
active?: boolean
|
|
23
|
+
variant?: 'default' | 'primary' | 'success' | 'danger'
|
|
24
|
+
showLabel?: boolean
|
|
25
|
+
disabled?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ToolbarButton = memo(function ToolbarButton({
|
|
29
|
+
icon,
|
|
30
|
+
label,
|
|
31
|
+
onClick,
|
|
32
|
+
active = false,
|
|
33
|
+
variant = 'default',
|
|
34
|
+
showLabel = true,
|
|
35
|
+
disabled = false,
|
|
36
|
+
}: ToolbarButtonProps) {
|
|
37
|
+
const variantStyles = {
|
|
38
|
+
default: active
|
|
39
|
+
? 'bg-white/20 text-white'
|
|
40
|
+
: 'text-white/70 hover:text-white hover:bg-white/10',
|
|
41
|
+
primary: active
|
|
42
|
+
? 'bg-primary/30 text-primary-foreground'
|
|
43
|
+
: 'text-white/70 hover:text-primary hover:bg-primary/10',
|
|
44
|
+
success: 'text-white/70 hover:text-emerald-400 hover:bg-emerald-500/10',
|
|
45
|
+
danger: 'text-white/70 hover:text-red-400 hover:bg-red-500/10',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const content = (
|
|
49
|
+
<>
|
|
50
|
+
{icon}
|
|
51
|
+
{showLabel && (
|
|
52
|
+
<span class="hidden sm:inline text-xs font-medium">{label}</span>
|
|
53
|
+
)}
|
|
54
|
+
</>
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
const class = cn(
|
|
58
|
+
'flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-200',
|
|
59
|
+
'focus:outline-none focus:ring-2 focus:ring-white/20',
|
|
60
|
+
disabled
|
|
61
|
+
? 'opacity-40 cursor-not-allowed text-white'
|
|
62
|
+
: variantStyles[variant],
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
onClick={disabled ? undefined : onClick}
|
|
69
|
+
class={cn(class, !disabled && 'cursor-pointer')}
|
|
70
|
+
title={label}
|
|
71
|
+
disabled={disabled}
|
|
72
|
+
>
|
|
73
|
+
{content}
|
|
74
|
+
</button>
|
|
75
|
+
)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
interface ExpandableSectionProps {
|
|
79
|
+
show: boolean
|
|
80
|
+
children: React.JSX.Element
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const ExpandableSection = memo(function ExpandableSection({
|
|
84
|
+
show,
|
|
85
|
+
children,
|
|
86
|
+
}: ExpandableSectionProps) {
|
|
87
|
+
let contentRef: HTMLDivElement | undefined
|
|
88
|
+
const [width, setWidth] = createSignal(0)
|
|
89
|
+
|
|
90
|
+
createEffect(() => {
|
|
91
|
+
if (contentRef) {
|
|
92
|
+
setWidth(show ? contentRef.scrollWidth : 0)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div
|
|
98
|
+
class="overflow-hidden transition-all duration-300 ease-out"
|
|
99
|
+
style={{ width: show ? width : 0, opacity: show ? 1 : 0 }}
|
|
100
|
+
>
|
|
101
|
+
<div
|
|
102
|
+
ref={contentRef}
|
|
103
|
+
class={cn(
|
|
104
|
+
'flex items-center gap-1 whitespace-nowrap',
|
|
105
|
+
'transition-transform duration-300 ease-out',
|
|
106
|
+
show ? 'translate-x-0' : '-translate-x-2',
|
|
107
|
+
)}
|
|
108
|
+
>
|
|
109
|
+
{children}
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
export interface DevToolbarProps {
|
|
116
|
+
/** Whether dev tools are allowed. Default: auto-detects from env */
|
|
117
|
+
isAllowed?: boolean
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const DevToolbar = memo(function DevToolbar({
|
|
121
|
+
isAllowed,
|
|
122
|
+
}: DevToolbarProps = {}) {
|
|
123
|
+
const isDevAllowed = createIsDevAllowed(isAllowed)
|
|
124
|
+
const devMode = createDevModeOptional()
|
|
125
|
+
const performanceContext = createPerformanceContextOptional()
|
|
126
|
+
|
|
127
|
+
const [isReady, setIsReady] = createSignal(false)
|
|
128
|
+
createEffect(() => {
|
|
129
|
+
const timer = setTimeout(() => setIsReady(true), 1000)
|
|
130
|
+
return () => clearTimeout(timer)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (!isDevAllowed || !devMode) return null
|
|
134
|
+
|
|
135
|
+
const {
|
|
136
|
+
isDevMode,
|
|
137
|
+
isSidebarOpen,
|
|
138
|
+
toggleDevMode,
|
|
139
|
+
toggleSidebar,
|
|
140
|
+
openIssueDialog,
|
|
141
|
+
issueDialog,
|
|
142
|
+
closeIssueDialog,
|
|
143
|
+
} = devMode
|
|
144
|
+
|
|
145
|
+
const showContent = isDevMode && isReady
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<>
|
|
149
|
+
{/* Floating Toolbar */}
|
|
150
|
+
<div
|
|
151
|
+
data-dev-tool="true"
|
|
152
|
+
class={cn(
|
|
153
|
+
'fixed bottom-4 left-1/2 -translate-x-1/2 z-50',
|
|
154
|
+
'flex items-center px-2 py-1',
|
|
155
|
+
showContent ? 'gap-2' : 'gap-0',
|
|
156
|
+
'rounded-2xl border border-white/10',
|
|
157
|
+
'bg-gradient-to-r from-slate-900/95 via-slate-800/95 to-slate-900/95',
|
|
158
|
+
'backdrop-blur-xl shadow-2xl shadow-black/50',
|
|
159
|
+
'transition-all duration-300 ease-out',
|
|
160
|
+
)}
|
|
161
|
+
>
|
|
162
|
+
{/* Power / Toggle Dev Mode */}
|
|
163
|
+
<ToolbarButton
|
|
164
|
+
icon={<Power class="w-4 h-4" />}
|
|
165
|
+
label={isDevMode ? 'Dev Mode On' : 'Dev Mode Off'}
|
|
166
|
+
onClick={toggleDevMode}
|
|
167
|
+
active={isDevMode}
|
|
168
|
+
variant={isDevMode ? 'primary' : 'default'}
|
|
169
|
+
/>
|
|
170
|
+
|
|
171
|
+
<ExpandableSection show={showContent}>
|
|
172
|
+
{/* Separator */}
|
|
173
|
+
<div class="shrink-0 w-px h-6 bg-white/20 mx-1 my-2" />
|
|
174
|
+
|
|
175
|
+
{/* Inspector Mode Toggle */}
|
|
176
|
+
<ToolbarButton
|
|
177
|
+
icon={<Target class="w-4 h-4" />}
|
|
178
|
+
label="Inspector"
|
|
179
|
+
onClick={devMode.toggleInspector}
|
|
180
|
+
active={devMode.isInspectorMode}
|
|
181
|
+
variant={devMode.isInspectorMode ? 'primary' : 'default'}
|
|
182
|
+
/>
|
|
183
|
+
|
|
184
|
+
{/* Performance Monitor Toggle */}
|
|
185
|
+
{performanceContext && (
|
|
186
|
+
<ToolbarButton
|
|
187
|
+
icon={<Activity class="w-4 h-4" />}
|
|
188
|
+
label="Perf"
|
|
189
|
+
onClick={performanceContext.togglePerformancePanel}
|
|
190
|
+
active={performanceContext.isPerformancePanelOpen}
|
|
191
|
+
variant={
|
|
192
|
+
performanceContext.isPerformancePanelOpen
|
|
193
|
+
? 'primary'
|
|
194
|
+
: 'default'
|
|
195
|
+
}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
198
|
+
|
|
199
|
+
{/* Sidebar Toggle (Desktop only) */}
|
|
200
|
+
<div class="hidden md:block">
|
|
201
|
+
<ToolbarButton
|
|
202
|
+
icon={<PanelRight class="w-4 h-4" />}
|
|
203
|
+
label="Issues"
|
|
204
|
+
onClick={toggleSidebar}
|
|
205
|
+
active={isSidebarOpen}
|
|
206
|
+
/>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
{/* Create Bug */}
|
|
210
|
+
<ToolbarButton
|
|
211
|
+
icon={<Bug class="w-4 h-4" />}
|
|
212
|
+
label="Bug"
|
|
213
|
+
onClick={() => openIssueDialog('bug')}
|
|
214
|
+
variant="danger"
|
|
215
|
+
/>
|
|
216
|
+
|
|
217
|
+
{/* Create Feature */}
|
|
218
|
+
<ToolbarButton
|
|
219
|
+
icon={<Lightbulb class="w-4 h-4" />}
|
|
220
|
+
label="Feature"
|
|
221
|
+
onClick={() => openIssueDialog('feature')}
|
|
222
|
+
variant="success"
|
|
223
|
+
/>
|
|
224
|
+
</ExpandableSection>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
{/* Performance Panel */}
|
|
228
|
+
{performanceContext && <PerformancePanel />}
|
|
229
|
+
|
|
230
|
+
{/* Issue Dialog */}
|
|
231
|
+
<div data-dev-tool="true">
|
|
232
|
+
<GitHubIssueDialog
|
|
233
|
+
open={issueDialog.isOpen}
|
|
234
|
+
onOpenChange={(open) => !open && closeIssueDialog()}
|
|
235
|
+
initialType={issueDialog.type}
|
|
236
|
+
initialDescription={issueDialog.initialDescription}
|
|
237
|
+
componentDetails={issueDialog.componentDetails}
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
</>
|
|
241
|
+
)
|
|
242
|
+
})
|