@geenius/tools 0.1.0 → 0.3.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.
Files changed (177) hide show
  1. package/package.json +62 -3
  2. package/packages/convex/shared/README.md +1 -1
  3. package/packages/devtools/dist/index.d.ts +204 -0
  4. package/packages/devtools/dist/index.js +186 -0
  5. package/packages/devtools/dist/index.js.map +1 -0
  6. package/packages/devtools/react/README.md +1 -1
  7. package/packages/devtools/solidjs/README.md +1 -1
  8. package/packages/devtools/solidjs/dist/index.js +1830 -0
  9. package/packages/devtools/solidjs/dist/index.js.map +1 -0
  10. package/packages/env/dist/index.d.ts +151 -0
  11. package/packages/env/dist/index.js +93 -0
  12. package/packages/env/dist/index.js.map +1 -0
  13. package/packages/errors/dist/index.d.ts +177 -0
  14. package/packages/errors/dist/index.js +187 -0
  15. package/packages/errors/dist/index.js.map +1 -0
  16. package/packages/errors/react/README.md +1 -1
  17. package/packages/errors/solidjs/README.md +1 -1
  18. package/packages/logger/dist/index.d.ts +171 -0
  19. package/packages/logger/dist/index.js +216 -0
  20. package/packages/logger/dist/index.js.map +1 -0
  21. package/packages/logger/react/README.md +1 -1
  22. package/packages/logger/solidjs/README.md +1 -1
  23. package/packages/perf/dist/index.d.ts +168 -0
  24. package/packages/perf/dist/index.js +265 -0
  25. package/packages/perf/dist/index.js.map +1 -0
  26. package/packages/perf/react/README.md +1 -1
  27. package/packages/perf/solidjs/README.md +1 -1
  28. package/packages/shared/dist/index.d.ts +253 -0
  29. package/packages/shared/dist/index.js +278 -0
  30. package/packages/shared/dist/index.js.map +1 -0
  31. package/.changeset/config.json +0 -11
  32. package/.env.example +0 -2
  33. package/.github/CODEOWNERS +0 -1
  34. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -16
  35. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -11
  36. package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
  37. package/.github/dependabot.yml +0 -11
  38. package/.github/workflows/ci.yml +0 -23
  39. package/.github/workflows/release.yml +0 -29
  40. package/.node-version +0 -1
  41. package/.nvmrc +0 -1
  42. package/.prettierrc +0 -7
  43. package/.project/ACCOUNT.yaml +0 -4
  44. package/.project/IDEAS.yaml +0 -7
  45. package/.project/PROJECT.yaml +0 -11
  46. package/.project/ROADMAP.yaml +0 -15
  47. package/CODE_OF_CONDUCT.md +0 -26
  48. package/CONTRIBUTING.md +0 -69
  49. package/SECURITY.md +0 -18
  50. package/SUPPORT.md +0 -14
  51. package/packages/convex/shared/package.json +0 -42
  52. package/packages/convex/shared/src/audit/index.ts +0 -5
  53. package/packages/convex/shared/src/audit/presets.ts +0 -165
  54. package/packages/convex/shared/src/audit/schema.ts +0 -85
  55. package/packages/convex/shared/src/audit/write.ts +0 -102
  56. package/packages/convex/shared/src/extract.ts +0 -75
  57. package/packages/convex/shared/src/index.ts +0 -41
  58. package/packages/convex/shared/src/messages.ts +0 -45
  59. package/packages/convex/shared/src/security.ts +0 -112
  60. package/packages/convex/shared/src/throw.ts +0 -184
  61. package/packages/convex/shared/src/types.ts +0 -57
  62. package/packages/convex/shared/src/utils.ts +0 -58
  63. package/packages/convex/shared/tsconfig.json +0 -28
  64. package/packages/convex/shared/tsup.config.ts +0 -12
  65. package/packages/devtools/package.json +0 -27
  66. package/packages/devtools/react/package.json +0 -53
  67. package/packages/devtools/react/src/components/DesignPreview.tsx +0 -59
  68. package/packages/devtools/react/src/components/DesignSwitcherDropdown.tsx +0 -99
  69. package/packages/devtools/react/src/components/DevSidebar.tsx +0 -247
  70. package/packages/devtools/react/src/components/DevToolbar.tsx +0 -242
  71. package/packages/devtools/react/src/components/GitHubIssueDialog.tsx +0 -402
  72. package/packages/devtools/react/src/components/InspectorOverlay.tsx +0 -312
  73. package/packages/devtools/react/src/components/PageLoadWaterfall.tsx +0 -144
  74. package/packages/devtools/react/src/components/PerformancePanel.tsx +0 -330
  75. package/packages/devtools/react/src/context/DevModeContext.tsx +0 -226
  76. package/packages/devtools/react/src/context/PerformanceContext.tsx +0 -143
  77. package/packages/devtools/react/src/data/designs.ts +0 -13
  78. package/packages/devtools/react/src/hooks/useGitHubLabels.ts +0 -47
  79. package/packages/devtools/react/src/hooks/useVirtualList.ts +0 -124
  80. package/packages/devtools/react/src/index.ts +0 -77
  81. package/packages/devtools/react/src/panels/ConvexSpy.tsx +0 -130
  82. package/packages/devtools/react/src/panels/DatabaseSeeder.tsx +0 -116
  83. package/packages/devtools/react/src/panels/DevModePhase2.tsx +0 -191
  84. package/packages/devtools/react/src/panels/DevModePhase3.tsx +0 -234
  85. package/packages/devtools/react/src/panels/FeatureFlagsToggle.tsx +0 -104
  86. package/packages/devtools/react/src/panels/QuickRouteJump.tsx +0 -152
  87. package/packages/devtools/react/src/services/github-service.ts +0 -247
  88. package/packages/devtools/react/tsconfig.json +0 -31
  89. package/packages/devtools/react/tsup.config.ts +0 -18
  90. package/packages/devtools/solidjs/package.json +0 -49
  91. package/packages/devtools/solidjs/src/components/DesignPreview.tsx +0 -51
  92. package/packages/devtools/solidjs/src/components/DesignSwitcherDropdown.tsx +0 -95
  93. package/packages/devtools/solidjs/src/components/DevSidebar.tsx +0 -247
  94. package/packages/devtools/solidjs/src/components/DevToolbar.tsx +0 -242
  95. package/packages/devtools/solidjs/src/components/GitHubIssueDialog.tsx +0 -400
  96. package/packages/devtools/solidjs/src/components/InspectorOverlay.tsx +0 -311
  97. package/packages/devtools/solidjs/src/components/PageLoadWaterfall.tsx +0 -144
  98. package/packages/devtools/solidjs/src/components/PerformancePanel.tsx +0 -330
  99. package/packages/devtools/solidjs/src/context/DevModeContext.tsx +0 -216
  100. package/packages/devtools/solidjs/src/context/PerformanceContext.tsx +0 -135
  101. package/packages/devtools/solidjs/src/data/designs.ts +0 -13
  102. package/packages/devtools/solidjs/src/hooks/createGitHubLabels.ts +0 -47
  103. package/packages/devtools/solidjs/src/index.ts +0 -64
  104. package/packages/devtools/solidjs/src/services/github-service.ts +0 -247
  105. package/packages/devtools/solidjs/tsconfig.json +0 -21
  106. package/packages/devtools/src/index.ts +0 -377
  107. package/packages/devtools/tsup.config.ts +0 -12
  108. package/packages/env/package.json +0 -30
  109. package/packages/env/src/index.ts +0 -264
  110. package/packages/env/tsup.config.ts +0 -12
  111. package/packages/errors/package.json +0 -27
  112. package/packages/errors/react/package.json +0 -72
  113. package/packages/errors/react/src/analytics.ts +0 -16
  114. package/packages/errors/react/src/components/ErrorBoundary.tsx +0 -248
  115. package/packages/errors/react/src/components/ErrorDisplay.tsx +0 -328
  116. package/packages/errors/react/src/components/ValidationErrors.tsx +0 -102
  117. package/packages/errors/react/src/config.ts +0 -199
  118. package/packages/errors/react/src/constants.ts +0 -74
  119. package/packages/errors/react/src/hooks/useErrorBoundary.ts +0 -92
  120. package/packages/errors/react/src/hooks/useErrorHandler.ts +0 -87
  121. package/packages/errors/react/src/index.ts +0 -96
  122. package/packages/errors/react/src/types.ts +0 -102
  123. package/packages/errors/react/src/utils/errorMessages.ts +0 -35
  124. package/packages/errors/react/src/utils/errorPolicy.ts +0 -139
  125. package/packages/errors/react/src/utils/extractAppError.ts +0 -174
  126. package/packages/errors/react/src/utils/formatError.ts +0 -112
  127. package/packages/errors/react/tsconfig.json +0 -25
  128. package/packages/errors/react/tsup.config.ts +0 -24
  129. package/packages/errors/solidjs/package.json +0 -46
  130. package/packages/errors/solidjs/src/components/ErrorDisplay.tsx +0 -179
  131. package/packages/errors/solidjs/src/config.ts +0 -98
  132. package/packages/errors/solidjs/src/hooks/createErrorHandler.ts +0 -107
  133. package/packages/errors/solidjs/src/index.ts +0 -61
  134. package/packages/errors/solidjs/src/types.ts +0 -34
  135. package/packages/errors/solidjs/src/utils/errorPolicy.ts +0 -56
  136. package/packages/errors/solidjs/src/utils/extractAppError.ts +0 -94
  137. package/packages/errors/solidjs/src/utils/formatError.ts +0 -33
  138. package/packages/errors/solidjs/tsconfig.json +0 -26
  139. package/packages/errors/solidjs/tsup.config.ts +0 -21
  140. package/packages/errors/src/index.ts +0 -320
  141. package/packages/errors/tsup.config.ts +0 -12
  142. package/packages/logger/package.json +0 -27
  143. package/packages/logger/react/package.json +0 -46
  144. package/packages/logger/react/src/index.ts +0 -4
  145. package/packages/logger/react/src/useMetrics.ts +0 -42
  146. package/packages/logger/react/src/usePerformanceLog.ts +0 -61
  147. package/packages/logger/react/tsconfig.json +0 -31
  148. package/packages/logger/react/tsup.config.ts +0 -12
  149. package/packages/logger/solidjs/package.json +0 -45
  150. package/packages/logger/solidjs/src/createMetrics.ts +0 -37
  151. package/packages/logger/solidjs/src/createPerformanceLog.ts +0 -58
  152. package/packages/logger/solidjs/src/index.ts +0 -4
  153. package/packages/logger/solidjs/tsconfig.json +0 -32
  154. package/packages/logger/solidjs/tsup.config.ts +0 -12
  155. package/packages/logger/src/index.ts +0 -363
  156. package/packages/logger/tsup.config.ts +0 -12
  157. package/packages/perf/package.json +0 -27
  158. package/packages/perf/react/package.json +0 -59
  159. package/packages/perf/react/src/components/PerformanceDashboard.tsx +0 -257
  160. package/packages/perf/react/src/hooks/useMonitoredQuery.ts +0 -89
  161. package/packages/perf/react/src/hooks/usePerformanceMetrics.ts +0 -78
  162. package/packages/perf/react/src/index.ts +0 -33
  163. package/packages/perf/react/src/services/PerformanceMonitor.ts +0 -313
  164. package/packages/perf/react/src/types.ts +0 -77
  165. package/packages/perf/react/tsconfig.json +0 -25
  166. package/packages/perf/react/tsup.config.ts +0 -19
  167. package/packages/perf/solidjs/package.json +0 -41
  168. package/packages/perf/solidjs/src/components/PerformanceDashboard.tsx +0 -207
  169. package/packages/perf/solidjs/src/hooks/createPerformanceMetrics.ts +0 -73
  170. package/packages/perf/solidjs/src/index.ts +0 -31
  171. package/packages/perf/solidjs/src/services/PerformanceMonitor.ts +0 -134
  172. package/packages/perf/solidjs/src/types.ts +0 -78
  173. package/packages/perf/solidjs/tsconfig.json +0 -26
  174. package/packages/perf/solidjs/tsup.config.ts +0 -14
  175. package/packages/perf/src/index.ts +0 -410
  176. package/packages/perf/tsup.config.ts +0 -12
  177. package/pnpm-workspace.yaml +0 -2
@@ -1,47 +0,0 @@
1
- // @geenius-tools/devtools-react — src/hooks/useGitHubLabels.ts
2
-
3
- import { useState, useEffect } from 'react'
4
- import {
5
- getRepositoryLabels,
6
- isGitHubConfigured,
7
- type GitHubLabel,
8
- } from '../services/github-service'
9
-
10
- /**
11
- * Hook to fetch GitHub repository labels.
12
- * Fetches once on mount if GitHub is configured.
13
- * No external dependencies (no react-query).
14
- */
15
- export function useGitHubLabels() {
16
- const [data, setData] = useState<GitHubLabel[] | undefined>(undefined)
17
- const [isLoading, setIsLoading] = useState(false)
18
- const [error, setError] = useState<Error | null>(null)
19
- const isConfigured = isGitHubConfigured()
20
-
21
- useEffect(() => {
22
- if (!isConfigured) return
23
-
24
- let cancelled = false
25
- setIsLoading(true)
26
-
27
- getRepositoryLabels()
28
- .then((labels) => {
29
- if (!cancelled) {
30
- setData(labels)
31
- setIsLoading(false)
32
- }
33
- })
34
- .catch((err) => {
35
- if (!cancelled) {
36
- setError(err)
37
- setIsLoading(false)
38
- }
39
- })
40
-
41
- return () => {
42
- cancelled = true
43
- }
44
- }, [isConfigured])
45
-
46
- return { data, isLoading, error, isConfigured }
47
- }
@@ -1,124 +0,0 @@
1
- import { useRef, useCallback, useMemo, type RefObject } from 'react'
2
-
3
- /**
4
- * TanStack Virtual wrapper — Unified virtual list hook.
5
- * Provides a simplified API over @tanstack/react-virtual for consistent usage.
6
- *
7
- * @example
8
- * ```tsx
9
- * const { virtualItems, totalSize, containerRef } = useVirtualList({
10
- * count: items.length,
11
- * estimateSize: () => 48,
12
- * })
13
- *
14
- * return (
15
- * <div ref={containerRef} style={{ height: 400, overflow: 'auto' }}>
16
- * <div style={{ height: totalSize }}>
17
- * {virtualItems.map(item => (
18
- * <div key={item.key} style={{ height: item.size, transform: `translateY(${item.start}px)` }}>
19
- * {items[item.index]}
20
- * </div>
21
- * ))}
22
- * </div>
23
- * </div>
24
- * )
25
- * ```
26
- */
27
-
28
- interface VirtualListOptions {
29
- count: number
30
- estimateSize: (index: number) => number
31
- overscan?: number
32
- paddingStart?: number
33
- paddingEnd?: number
34
- horizontal?: boolean
35
- }
36
-
37
- interface VirtualItem {
38
- key: string
39
- index: number
40
- start: number
41
- size: number
42
- end: number
43
- }
44
-
45
- interface VirtualListResult {
46
- virtualItems: VirtualItem[]
47
- totalSize: number
48
- containerRef: RefObject<HTMLDivElement | null>
49
- scrollToIndex: (index: number) => void
50
- scrollToOffset: (offset: number) => void
51
- }
52
-
53
- /**
54
- * Simplified virtual list hook.
55
- *
56
- * On environments where @tanstack/react-virtual is installed,
57
- * use that directly for full feature set. This provides a
58
- * fallback implementation for basic virtualization.
59
- */
60
- export function useVirtualList(options: VirtualListOptions): VirtualListResult {
61
- const containerRef = useRef<HTMLDivElement>(null)
62
-
63
- // Calculate sizes
64
- const sizes = useMemo(() => {
65
- return Array.from({ length: options.count }, (_, i) => options.estimateSize(i))
66
- }, [options.count, options.estimateSize])
67
-
68
- const offsets = useMemo(() => {
69
- const result: number[] = []
70
- let offset = options.paddingStart ?? 0
71
- for (const size of sizes) {
72
- result.push(offset)
73
- offset += size
74
- }
75
- return result
76
- }, [sizes, options.paddingStart])
77
-
78
- const totalSize = useMemo(() => {
79
- return (offsets[offsets.length - 1] ?? 0) + (sizes[sizes.length - 1] ?? 0) + (options.paddingEnd ?? 0)
80
- }, [offsets, sizes, options.paddingEnd])
81
-
82
- // Simple window calculation (fallback — use TanStack Virtual for scroll-aware virtualization)
83
- const virtualItems = useMemo((): VirtualItem[] => {
84
- const overscan = options.overscan ?? 5
85
- // Without scroll tracking, render all items (or first batch)
86
- // In real usage, this would be scroll-aware via @tanstack/react-virtual
87
- const startIdx = 0
88
- const endIdx = Math.min(options.count, 100) // Render first 100 as fallback
89
-
90
- return Array.from({ length: endIdx - startIdx }, (_, i) => {
91
- const index = startIdx + i
92
- return {
93
- key: `v_${index}`,
94
- index,
95
- start: offsets[index] ?? 0,
96
- size: sizes[index] ?? 0,
97
- end: (offsets[index] ?? 0) + (sizes[index] ?? 0),
98
- }
99
- })
100
- }, [options.count, options.overscan, offsets, sizes])
101
-
102
- const scrollToIndex = useCallback((index: number) => {
103
- const offset = offsets[index]
104
- if (offset !== undefined && containerRef.current) {
105
- if (options.horizontal) {
106
- containerRef.current.scrollLeft = offset
107
- } else {
108
- containerRef.current.scrollTop = offset
109
- }
110
- }
111
- }, [offsets, options.horizontal])
112
-
113
- const scrollToOffset = useCallback((offset: number) => {
114
- if (containerRef.current) {
115
- if (options.horizontal) {
116
- containerRef.current.scrollLeft = offset
117
- } else {
118
- containerRef.current.scrollTop = offset
119
- }
120
- }
121
- }, [options.horizontal])
122
-
123
- return { virtualItems, totalSize, containerRef, scrollToIndex, scrollToOffset }
124
- }
@@ -1,77 +0,0 @@
1
- // @geenius-tools/devtools-react — src/index.ts
2
-
3
- // Components
4
- export { DevToolbar } from './components/DevToolbar'
5
- export type { DevToolbarProps } from './components/DevToolbar'
6
- export { DevSidebar } from './components/DevSidebar'
7
- export { PerformancePanel } from './components/PerformancePanel'
8
- export { InspectorOverlay } from './components/InspectorOverlay'
9
- export { GitHubIssueDialog } from './components/GitHubIssueDialog'
10
- export type { GitHubIssueDialogProps } from './components/GitHubIssueDialog'
11
- export { DesignPreview } from './components/DesignPreview'
12
- export type { DesignPreviewProps } from './components/DesignPreview'
13
- export { DesignSwitcherDropdown } from './components/DesignSwitcherDropdown'
14
- export type { DesignSwitcherDropdownProps } from './components/DesignSwitcherDropdown'
15
- export { PageLoadWaterfall, logWaterfall } from './components/PageLoadWaterfall'
16
- export type { SsrTiming } from './components/PageLoadWaterfall'
17
-
18
- // Data
19
- export { designs } from './data/designs'
20
- export type { Design } from './data/designs'
21
-
22
- // Context
23
- export {
24
- DevModeProvider,
25
- useDevMode,
26
- useDevModeOptional,
27
- useIsDevToolsVisible,
28
- useIsDevAllowed,
29
- } from './context/DevModeContext'
30
- export type {
31
- DevModeProviderProps,
32
- DevModeContextValue,
33
- ComponentDetails,
34
- } from './context/DevModeContext'
35
-
36
- export {
37
- PerformanceProvider,
38
- usePerformanceContext,
39
- usePerformanceContextOptional,
40
- } from './context/PerformanceContext'
41
-
42
- // Services
43
- export {
44
- configureGitHub,
45
- isGitHubConfigured,
46
- getGitHubConfig,
47
- createGitHubIssue,
48
- getRepositoryLabels,
49
- getGitHubIssues,
50
- generateContextInfo,
51
- getIssueType,
52
- formatRelativeTime,
53
- } from './services/github-service'
54
- export type {
55
- GitHubConfig,
56
- GitHubIssue,
57
- GitHubLabel,
58
- GitHubIssueListItem,
59
- CreateIssueResponse,
60
- IssueFilter,
61
- } from './services/github-service'
62
-
63
- // Hooks
64
- export { useGitHubLabels } from './hooks/useGitHubLabels'
65
- export { useVirtualList } from './hooks/useVirtualList'
66
-
67
- // Dev Mode Phase 1 Panels
68
- export { ConvexSpyPanel, useConvexSpy } from './panels/ConvexSpy'
69
- export { DatabaseSeeder } from './panels/DatabaseSeeder'
70
- export { FeatureFlagsToggle } from './panels/FeatureFlagsToggle'
71
- export { QuickRouteJump } from './panels/QuickRouteJump'
72
-
73
- // Dev Mode Phase 2 Panels
74
- export { UserImpersonation, ThemeGenerator } from './panels/DevModePhase2'
75
-
76
- // Dev Mode Phase 3 Panels
77
- export { AccessibilityAuditor, ResponsiveTester } from './panels/DevModePhase3'
@@ -1,130 +0,0 @@
1
- import { useState, useEffect, useCallback, useRef, type ReactNode } from 'react'
2
-
3
- // ─── Types ────────────────────────────────────────────────────
4
-
5
- interface ConvexEvent {
6
- id: string
7
- type: 'query' | 'mutation' | 'action'
8
- name: string
9
- args: unknown
10
- result?: unknown
11
- error?: string
12
- duration: number
13
- timestamp: number
14
- status: 'pending' | 'success' | 'error'
15
- }
16
-
17
- interface ConvexSpyContextValue {
18
- events: ConvexEvent[]
19
- isRecording: boolean
20
- toggleRecording: () => void
21
- clear: () => void
22
- filter: string
23
- setFilter: (f: string) => void
24
- }
25
-
26
- // ─── Hook ─────────────────────────────────────────────────────
27
-
28
- /**
29
- * Hook to spy on Convex queries and mutations in real-time.
30
- * Intercepts the Convex client's internal transport for debugging.
31
- */
32
- export function useConvexSpy(maxEvents = 200): ConvexSpyContextValue {
33
- const [events, setEvents] = useState<ConvexEvent[]>([])
34
- const [isRecording, setIsRecording] = useState(true)
35
- const [filter, setFilter] = useState('')
36
- const nextId = useRef(0)
37
-
38
- const addEvent = useCallback((event: Omit<ConvexEvent, 'id'>) => {
39
- if (!isRecording) return
40
- setEvents(prev => {
41
- const next = [{ ...event, id: `ev_${nextId.current++}` }, ...prev]
42
- return next.slice(0, maxEvents)
43
- })
44
- }, [isRecording, maxEvents])
45
-
46
- // Monkey-patch console to capture Convex debug logs
47
- useEffect(() => {
48
- if (!isRecording) return
49
- const origDebug = console.debug
50
- console.debug = (...args: unknown[]) => {
51
- origDebug.apply(console, args)
52
- const msg = String(args[0] ?? '')
53
- if (msg.includes('[Convex]') || msg.includes('convex:')) {
54
- const isQuery = msg.includes('query') || msg.includes('Query')
55
- const isMutation = msg.includes('mutation') || msg.includes('Mutation')
56
- const type = isMutation ? 'mutation' : isQuery ? 'query' : 'action'
57
- addEvent({
58
- type,
59
- name: extractFunctionName(msg),
60
- args: args.length > 1 ? args[1] : undefined,
61
- duration: 0,
62
- timestamp: Date.now(),
63
- status: msg.includes('error') || msg.includes('Error') ? 'error' : 'success',
64
- })
65
- }
66
- }
67
- return () => { console.debug = origDebug }
68
- }, [isRecording, addEvent])
69
-
70
- return {
71
- events: filter ? events.filter(e => e.name.toLowerCase().includes(filter.toLowerCase())) : events,
72
- isRecording,
73
- toggleRecording: () => setIsRecording(p => !p),
74
- clear: () => setEvents([]),
75
- filter,
76
- setFilter,
77
- }
78
- }
79
-
80
- function extractFunctionName(msg: string): string {
81
- const match = msg.match(/(?:query|mutation|action)\s*:\s*(\S+)/i) ?? msg.match(/\[(\w+(?:\.\w+)+)\]/)
82
- return match?.[1] ?? msg.slice(0, 50)
83
- }
84
-
85
- // ─── Component ────────────────────────────────────────────────
86
-
87
- /**
88
- * ConvexSpy Panel — Real-time monitoring of Convex queries/mutations.
89
- */
90
- export function ConvexSpyPanel() {
91
- const spy = useConvexSpy()
92
-
93
- return (
94
- <div style={{ fontFamily: 'monospace', fontSize: 12, color: '#e2e8f0', background: '#0f172a', borderRadius: 8, overflow: 'hidden' }}>
95
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', background: '#1e293b', borderBottom: '1px solid #334155' }}>
96
- <span style={{ fontWeight: 700, fontSize: 13 }}>🔍 Convex Spy</span>
97
- <div style={{ display: 'flex', gap: 6 }}>
98
- <input
99
- placeholder="Filter..."
100
- value={spy.filter}
101
- onChange={e => spy.setFilter(e.target.value)}
102
- style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', fontSize: 11, width: 120 }}
103
- />
104
- <button onClick={spy.toggleRecording} style={btnStyle}>{spy.isRecording ? '⏸' : '▶'}</button>
105
- <button onClick={spy.clear} style={btnStyle}>🗑</button>
106
- </div>
107
- </div>
108
- <div style={{ maxHeight: 300, overflow: 'auto', padding: 4 }}>
109
- {spy.events.length === 0 ? (
110
- <div style={{ padding: 16, textAlign: 'center', color: '#64748b' }}>
111
- {spy.isRecording ? 'Waiting for Convex events...' : 'Recording paused'}
112
- </div>
113
- ) : spy.events.map(e => (
114
- <div key={e.id} style={{ display: 'flex', gap: 8, padding: '4px 8px', borderBottom: '1px solid #1e293b', alignItems: 'center' }}>
115
- <span style={{ color: typeColor(e.type), fontWeight: 700, width: 60, textTransform: 'uppercase', fontSize: 10 }}>{e.type}</span>
116
- <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{e.name}</span>
117
- <span style={{ color: e.status === 'error' ? '#ef4444' : '#22c55e', fontSize: 10 }}>{e.status}</span>
118
- <span style={{ color: '#64748b', fontSize: 10, width: 60, textAlign: 'right' }}>{new Date(e.timestamp).toLocaleTimeString()}</span>
119
- </div>
120
- ))}
121
- </div>
122
- <div style={{ padding: '6px 12px', borderTop: '1px solid #334155', color: '#64748b', fontSize: 10 }}>
123
- {spy.events.length} events
124
- </div>
125
- </div>
126
- )
127
- }
128
-
129
- const btnStyle: React.CSSProperties = { padding: '4px 8px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', cursor: 'pointer', fontSize: 11 }
130
- const typeColor = (t: string) => t === 'mutation' ? '#f59e0b' : t === 'query' ? '#06b6d4' : '#a78bfa'
@@ -1,116 +0,0 @@
1
- import { useState, useCallback } from 'react'
2
-
3
- // ─── Types ────────────────────────────────────────────────────
4
-
5
- interface SeedConfig {
6
- name: string
7
- description: string
8
- tables: { name: string; count: number }[]
9
- generate: () => Promise<void>
10
- }
11
-
12
- interface DatabaseSeederProps {
13
- seeds: SeedConfig[]
14
- onReset?: () => Promise<void>
15
- }
16
-
17
- // ─── Component ────────────────────────────────────────────────
18
-
19
- /**
20
- * Database Seeder Panel — One-click data population and reset.
21
- * Provide seed configurations and the panel handles execution.
22
- *
23
- * @example
24
- * ```tsx
25
- * <DatabaseSeeder seeds={[
26
- * { name: 'Demo Data', description: '20 tasks, 5 users', tables: [...], generate: async () => { ... } }
27
- * ]} />
28
- * ```
29
- */
30
- export function DatabaseSeeder({ seeds, onReset }: DatabaseSeederProps) {
31
- const [isSeeding, setIsSeeding] = useState(false)
32
- const [activeSeed, setActiveSeed] = useState<string | null>(null)
33
- const [log, setLog] = useState<string[]>([])
34
-
35
- const runSeed = useCallback(async (seed: SeedConfig) => {
36
- setIsSeeding(true)
37
- setActiveSeed(seed.name)
38
- setLog(prev => [...prev, `🌱 Seeding: ${seed.name}...`])
39
- try {
40
- await seed.generate()
41
- setLog(prev => [...prev, `✅ ${seed.name} complete — ${seed.tables.map(t => `${t.count} ${t.name}`).join(', ')}`])
42
- } catch (err) {
43
- setLog(prev => [...prev, `❌ Failed: ${err instanceof Error ? err.message : String(err)}`])
44
- } finally {
45
- setIsSeeding(false)
46
- setActiveSeed(null)
47
- }
48
- }, [])
49
-
50
- const handleReset = useCallback(async () => {
51
- if (!onReset || !confirm('This will delete all data. Are you sure?')) return
52
- setIsSeeding(true)
53
- setLog(prev => [...prev, '🗑 Resetting database...'])
54
- try {
55
- await onReset()
56
- setLog(prev => [...prev, '✅ Database reset complete'])
57
- } catch (err) {
58
- setLog(prev => [...prev, `❌ Reset failed: ${err instanceof Error ? err.message : String(err)}`])
59
- } finally {
60
- setIsSeeding(false)
61
- }
62
- }, [onReset])
63
-
64
- return (
65
- <div style={{ fontFamily: 'system-ui', fontSize: 13, color: '#e2e8f0', background: '#0f172a', borderRadius: 8, overflow: 'hidden' }}>
66
- <div style={{ padding: '8px 12px', background: '#1e293b', borderBottom: '1px solid #334155', fontWeight: 700, fontSize: 13 }}>
67
- 🌱 Database Seeder
68
- </div>
69
- <div style={{ padding: 12 }}>
70
- <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 12 }}>
71
- {seeds.map(seed => (
72
- <button
73
- key={seed.name}
74
- disabled={isSeeding}
75
- onClick={() => runSeed(seed)}
76
- style={{
77
- padding: '8px 16px', borderRadius: 6, border: '1px solid #334155',
78
- background: activeSeed === seed.name ? '#059669' : '#1e293b',
79
- color: '#e2e8f0', cursor: isSeeding ? 'wait' : 'pointer', fontWeight: 600, fontSize: 12,
80
- }}
81
- >
82
- {activeSeed === seed.name ? '⏳ Seeding...' : seed.name}
83
- </button>
84
- ))}
85
- {onReset && (
86
- <button
87
- disabled={isSeeding}
88
- onClick={handleReset}
89
- style={{
90
- padding: '8px 16px', borderRadius: 6, border: '1px solid #ef4444',
91
- background: 'transparent', color: '#ef4444', cursor: isSeeding ? 'wait' : 'pointer',
92
- fontWeight: 600, fontSize: 12,
93
- }}
94
- >
95
- Reset All
96
- </button>
97
- )}
98
- </div>
99
- {seeds.map(seed => (
100
- <div key={seed.name} style={{ fontSize: 11, color: '#94a3b8', marginBottom: 4 }}>
101
- <strong>{seed.name}</strong>: {seed.description}
102
- </div>
103
- ))}
104
- </div>
105
- {log.length > 0 && (
106
- <div style={{ borderTop: '1px solid #334155', padding: 8, maxHeight: 150, overflow: 'auto', fontFamily: 'monospace', fontSize: 11 }}>
107
- {log.map((entry, i) => (
108
- <div key={i} style={{ padding: '2px 4px', color: entry.includes('❌') ? '#ef4444' : entry.includes('✅') ? '#22c55e' : '#94a3b8' }}>
109
- {entry}
110
- </div>
111
- ))}
112
- </div>
113
- )}
114
- </div>
115
- )
116
- }
@@ -1,191 +0,0 @@
1
- import { useState, useCallback, useMemo } from 'react'
2
-
3
- // ─── User Impersonation Panel ────────────────────────────────
4
-
5
- interface UserProfile {
6
- id: string
7
- name: string
8
- email: string
9
- role: string
10
- avatar?: string
11
- metadata?: Record<string, unknown>
12
- }
13
-
14
- interface UserImpersonationProps {
15
- users: UserProfile[]
16
- currentUserId: string
17
- onImpersonate: (userId: string) => void | Promise<void>
18
- onStopImpersonation: () => void | Promise<void>
19
- }
20
-
21
- /**
22
- * User Impersonation Panel — Quick role switching for testing.
23
- * Shows a list of available users with role badges and one-click switching.
24
- */
25
- export function UserImpersonation({ users, currentUserId, onImpersonate, onStopImpersonation }: UserImpersonationProps) {
26
- const [isImpersonating, setIsImpersonating] = useState(false)
27
- const [originalUserId] = useState(currentUserId)
28
- const [filter, setFilter] = useState('')
29
-
30
- const filtered = filter
31
- ? users.filter(u => u.name.toLowerCase().includes(filter.toLowerCase()) || u.role.toLowerCase().includes(filter.toLowerCase()))
32
- : users
33
-
34
- const handleImpersonate = useCallback(async (userId: string) => {
35
- await onImpersonate(userId)
36
- setIsImpersonating(true)
37
- }, [onImpersonate])
38
-
39
- const handleStop = useCallback(async () => {
40
- await onStopImpersonation()
41
- setIsImpersonating(false)
42
- }, [onStopImpersonation])
43
-
44
- const roleColors: Record<string, string> = {
45
- admin: '#ef4444', owner: '#f59e0b', member: '#3b82f6', viewer: '#22c55e', guest: '#94a3b8',
46
- }
47
-
48
- return (
49
- <div style={{ fontFamily: 'system-ui', fontSize: 13, color: '#e2e8f0', background: '#0f172a', borderRadius: 8, overflow: 'hidden' }}>
50
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', background: '#1e293b', borderBottom: '1px solid #334155' }}>
51
- <span style={{ fontWeight: 700, fontSize: 13 }}>👤 User Impersonation</span>
52
- <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
53
- {isImpersonating && (
54
- <button onClick={handleStop} style={{ padding: '4px 10px', borderRadius: 4, border: '1px solid #ef4444', background: 'transparent', color: '#ef4444', cursor: 'pointer', fontSize: 11, fontWeight: 600 }}>
55
- Stop
56
- </button>
57
- )}
58
- <input
59
- placeholder="Filter users..."
60
- value={filter}
61
- onChange={e => setFilter(e.target.value)}
62
- style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', fontSize: 11, width: 120 }}
63
- />
64
- </div>
65
- </div>
66
- {isImpersonating && (
67
- <div style={{ padding: '6px 12px', background: '#7c2d12', fontSize: 11, fontWeight: 600 }}>
68
- ⚠️ Currently impersonating — actions will execute as this user
69
- </div>
70
- )}
71
- <div style={{ maxHeight: 280, overflow: 'auto', padding: 4 }}>
72
- {filtered.map(user => (
73
- <div
74
- key={user.id}
75
- onClick={() => handleImpersonate(user.id)}
76
- style={{
77
- display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', borderRadius: 6, cursor: 'pointer',
78
- background: user.id === currentUserId ? '#1e293b' : 'transparent',
79
- borderLeft: user.id === currentUserId ? '3px solid #6366f1' : '3px solid transparent',
80
- }}
81
- >
82
- <div style={{ width: 32, height: 32, borderRadius: '50%', background: '#334155', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, fontWeight: 700 }}>
83
- {user.avatar ? <img src={user.avatar} alt="" style={{ width: 32, height: 32, borderRadius: '50%' }} /> : user.name[0]}
84
- </div>
85
- <div style={{ flex: 1 }}>
86
- <div style={{ fontWeight: 600, fontSize: 13 }}>
87
- {user.name} {user.id === originalUserId && <span style={{ fontSize: 10, color: '#64748b' }}>(you)</span>}
88
- </div>
89
- <div style={{ fontSize: 11, color: '#64748b' }}>{user.email}</div>
90
- </div>
91
- <span style={{ padding: '2px 8px', borderRadius: 10, fontSize: 10, fontWeight: 700, background: roleColors[user.role] ?? '#64748b', color: 'white', textTransform: 'uppercase' }}>
92
- {user.role}
93
- </span>
94
- </div>
95
- ))}
96
- </div>
97
- </div>
98
- )
99
- }
100
-
101
- // ─── Theme Generator Panel ───────────────────────────────────
102
-
103
- interface ThemeVariable {
104
- key: string
105
- label: string
106
- type: 'color' | 'size' | 'font' | 'number'
107
- value: string
108
- category: string
109
- }
110
-
111
- interface ThemeGeneratorProps {
112
- variables: ThemeVariable[]
113
- onUpdate: (key: string, value: string) => void
114
- onExport: (variables: ThemeVariable[]) => void
115
- onReset: () => void
116
- }
117
-
118
- /**
119
- * White-Label Theme Generator — Interactive theming playground.
120
- * Live-edit CSS variables with instant preview.
121
- */
122
- export function ThemeGenerator({ variables, onUpdate, onExport, onReset }: ThemeGeneratorProps) {
123
- const [search, setSearch] = useState('')
124
-
125
- const categories = useMemo(() => {
126
- const cats = new Map<string, ThemeVariable[]>()
127
- const filtered = search
128
- ? variables.filter(v => v.label.toLowerCase().includes(search.toLowerCase()) || v.key.toLowerCase().includes(search.toLowerCase()))
129
- : variables
130
- for (const v of filtered) {
131
- if (!cats.has(v.category)) cats.set(v.category, [])
132
- cats.get(v.category)!.push(v)
133
- }
134
- return cats
135
- }, [variables, search])
136
-
137
- return (
138
- <div style={{ fontFamily: 'system-ui', fontSize: 13, color: '#e2e8f0', background: '#0f172a', borderRadius: 8, overflow: 'hidden' }}>
139
- <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', background: '#1e293b', borderBottom: '1px solid #334155' }}>
140
- <span style={{ fontWeight: 700, fontSize: 13 }}>🎨 Theme Generator</span>
141
- <div style={{ display: 'flex', gap: 6 }}>
142
- <input
143
- placeholder="Search..."
144
- value={search}
145
- onChange={e => setSearch(e.target.value)}
146
- style={{ padding: '4px 8px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', fontSize: 11, width: 100 }}
147
- />
148
- <button onClick={() => onExport(variables)} style={btn}>Export</button>
149
- <button onClick={onReset} style={btn}>Reset</button>
150
- </div>
151
- </div>
152
- <div style={{ maxHeight: 400, overflow: 'auto', padding: 8 }}>
153
- {[...categories.entries()].map(([cat, vars]) => (
154
- <div key={cat}>
155
- <div style={{ padding: '8px 6px 4px', color: '#64748b', fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: 1 }}>{cat}</div>
156
- {vars.map(v => (
157
- <div key={v.key} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 6px' }}>
158
- <span style={{ flex: 1, fontSize: 12 }}>{v.label}</span>
159
- {v.type === 'color' ? (
160
- <input
161
- type="color"
162
- value={v.value}
163
- onChange={e => onUpdate(v.key, e.target.value)}
164
- style={{ width: 32, height: 24, border: 'none', borderRadius: 4, cursor: 'pointer' }}
165
- />
166
- ) : v.type === 'number' ? (
167
- <input
168
- type="number"
169
- value={v.value}
170
- onChange={e => onUpdate(v.key, e.target.value)}
171
- style={{ width: 60, padding: '2px 6px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', fontSize: 11 }}
172
- />
173
- ) : (
174
- <input
175
- type="text"
176
- value={v.value}
177
- onChange={e => onUpdate(v.key, e.target.value)}
178
- style={{ width: 120, padding: '2px 6px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', fontSize: 11 }}
179
- />
180
- )}
181
- <code style={{ fontSize: 9, color: '#64748b', maxWidth: 80, overflow: 'hidden', textOverflow: 'ellipsis' }}>{v.key}</code>
182
- </div>
183
- ))}
184
- </div>
185
- ))}
186
- </div>
187
- </div>
188
- )
189
- }
190
-
191
- const btn: React.CSSProperties = { padding: '4px 10px', borderRadius: 4, border: '1px solid #334155', background: '#0f172a', color: '#e2e8f0', cursor: 'pointer', fontSize: 11, fontWeight: 600 }