@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.
Files changed (160) hide show
  1. package/.changeset/config.json +11 -0
  2. package/.env.example +2 -0
  3. package/.github/CODEOWNERS +1 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +11 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +10 -0
  7. package/.github/dependabot.yml +11 -0
  8. package/.github/workflows/ci.yml +23 -0
  9. package/.github/workflows/release.yml +29 -0
  10. package/.node-version +1 -0
  11. package/.nvmrc +1 -0
  12. package/.prettierrc +7 -0
  13. package/.project/ACCOUNT.yaml +4 -0
  14. package/.project/IDEAS.yaml +7 -0
  15. package/.project/PROJECT.yaml +11 -0
  16. package/.project/ROADMAP.yaml +15 -0
  17. package/CHANGELOG.md +16 -0
  18. package/CODE_OF_CONDUCT.md +26 -0
  19. package/CONTRIBUTING.md +69 -0
  20. package/LICENSE +21 -0
  21. package/README.md +1 -0
  22. package/SECURITY.md +18 -0
  23. package/SUPPORT.md +14 -0
  24. package/package.json +75 -0
  25. package/packages/convex/shared/README.md +1 -0
  26. package/packages/convex/shared/package.json +42 -0
  27. package/packages/convex/shared/src/audit/index.ts +5 -0
  28. package/packages/convex/shared/src/audit/presets.ts +165 -0
  29. package/packages/convex/shared/src/audit/schema.ts +85 -0
  30. package/packages/convex/shared/src/audit/write.ts +102 -0
  31. package/packages/convex/shared/src/extract.ts +75 -0
  32. package/packages/convex/shared/src/index.ts +41 -0
  33. package/packages/convex/shared/src/messages.ts +45 -0
  34. package/packages/convex/shared/src/security.ts +112 -0
  35. package/packages/convex/shared/src/throw.ts +184 -0
  36. package/packages/convex/shared/src/types.ts +57 -0
  37. package/packages/convex/shared/src/utils.ts +58 -0
  38. package/packages/convex/shared/tsconfig.json +28 -0
  39. package/packages/convex/shared/tsup.config.ts +12 -0
  40. package/packages/devtools/package.json +27 -0
  41. package/packages/devtools/react/README.md +1 -0
  42. package/packages/devtools/react/package.json +53 -0
  43. package/packages/devtools/react/src/components/DesignPreview.tsx +59 -0
  44. package/packages/devtools/react/src/components/DesignSwitcherDropdown.tsx +99 -0
  45. package/packages/devtools/react/src/components/DevSidebar.tsx +247 -0
  46. package/packages/devtools/react/src/components/DevToolbar.tsx +242 -0
  47. package/packages/devtools/react/src/components/GitHubIssueDialog.tsx +402 -0
  48. package/packages/devtools/react/src/components/InspectorOverlay.tsx +312 -0
  49. package/packages/devtools/react/src/components/PageLoadWaterfall.tsx +144 -0
  50. package/packages/devtools/react/src/components/PerformancePanel.tsx +330 -0
  51. package/packages/devtools/react/src/context/DevModeContext.tsx +226 -0
  52. package/packages/devtools/react/src/context/PerformanceContext.tsx +143 -0
  53. package/packages/devtools/react/src/data/designs.ts +13 -0
  54. package/packages/devtools/react/src/hooks/useGitHubLabels.ts +47 -0
  55. package/packages/devtools/react/src/hooks/useVirtualList.ts +124 -0
  56. package/packages/devtools/react/src/index.ts +77 -0
  57. package/packages/devtools/react/src/panels/ConvexSpy.tsx +130 -0
  58. package/packages/devtools/react/src/panels/DatabaseSeeder.tsx +116 -0
  59. package/packages/devtools/react/src/panels/DevModePhase2.tsx +191 -0
  60. package/packages/devtools/react/src/panels/DevModePhase3.tsx +234 -0
  61. package/packages/devtools/react/src/panels/FeatureFlagsToggle.tsx +104 -0
  62. package/packages/devtools/react/src/panels/QuickRouteJump.tsx +152 -0
  63. package/packages/devtools/react/src/services/github-service.ts +247 -0
  64. package/packages/devtools/react/tsconfig.json +31 -0
  65. package/packages/devtools/react/tsup.config.ts +18 -0
  66. package/packages/devtools/solidjs/README.md +1 -0
  67. package/packages/devtools/solidjs/package.json +49 -0
  68. package/packages/devtools/solidjs/src/components/DesignPreview.tsx +51 -0
  69. package/packages/devtools/solidjs/src/components/DesignSwitcherDropdown.tsx +95 -0
  70. package/packages/devtools/solidjs/src/components/DevSidebar.tsx +247 -0
  71. package/packages/devtools/solidjs/src/components/DevToolbar.tsx +242 -0
  72. package/packages/devtools/solidjs/src/components/GitHubIssueDialog.tsx +400 -0
  73. package/packages/devtools/solidjs/src/components/InspectorOverlay.tsx +311 -0
  74. package/packages/devtools/solidjs/src/components/PageLoadWaterfall.tsx +144 -0
  75. package/packages/devtools/solidjs/src/components/PerformancePanel.tsx +330 -0
  76. package/packages/devtools/solidjs/src/context/DevModeContext.tsx +216 -0
  77. package/packages/devtools/solidjs/src/context/PerformanceContext.tsx +135 -0
  78. package/packages/devtools/solidjs/src/data/designs.ts +13 -0
  79. package/packages/devtools/solidjs/src/hooks/createGitHubLabels.ts +47 -0
  80. package/packages/devtools/solidjs/src/index.ts +64 -0
  81. package/packages/devtools/solidjs/src/services/github-service.ts +247 -0
  82. package/packages/devtools/solidjs/tsconfig.json +21 -0
  83. package/packages/devtools/src/index.ts +377 -0
  84. package/packages/devtools/tsup.config.ts +12 -0
  85. package/packages/env/package.json +30 -0
  86. package/packages/env/src/index.ts +264 -0
  87. package/packages/env/tsup.config.ts +12 -0
  88. package/packages/errors/package.json +27 -0
  89. package/packages/errors/react/README.md +1 -0
  90. package/packages/errors/react/package.json +72 -0
  91. package/packages/errors/react/src/analytics.ts +16 -0
  92. package/packages/errors/react/src/components/ErrorBoundary.tsx +248 -0
  93. package/packages/errors/react/src/components/ErrorDisplay.tsx +328 -0
  94. package/packages/errors/react/src/components/ValidationErrors.tsx +102 -0
  95. package/packages/errors/react/src/config.ts +199 -0
  96. package/packages/errors/react/src/constants.ts +74 -0
  97. package/packages/errors/react/src/hooks/useErrorBoundary.ts +92 -0
  98. package/packages/errors/react/src/hooks/useErrorHandler.ts +87 -0
  99. package/packages/errors/react/src/index.ts +96 -0
  100. package/packages/errors/react/src/types.ts +102 -0
  101. package/packages/errors/react/src/utils/errorMessages.ts +35 -0
  102. package/packages/errors/react/src/utils/errorPolicy.ts +139 -0
  103. package/packages/errors/react/src/utils/extractAppError.ts +174 -0
  104. package/packages/errors/react/src/utils/formatError.ts +112 -0
  105. package/packages/errors/react/tsconfig.json +25 -0
  106. package/packages/errors/react/tsup.config.ts +24 -0
  107. package/packages/errors/solidjs/README.md +1 -0
  108. package/packages/errors/solidjs/package.json +46 -0
  109. package/packages/errors/solidjs/src/components/ErrorDisplay.tsx +179 -0
  110. package/packages/errors/solidjs/src/config.ts +98 -0
  111. package/packages/errors/solidjs/src/hooks/createErrorHandler.ts +107 -0
  112. package/packages/errors/solidjs/src/index.ts +61 -0
  113. package/packages/errors/solidjs/src/types.ts +34 -0
  114. package/packages/errors/solidjs/src/utils/errorPolicy.ts +56 -0
  115. package/packages/errors/solidjs/src/utils/extractAppError.ts +94 -0
  116. package/packages/errors/solidjs/src/utils/formatError.ts +33 -0
  117. package/packages/errors/solidjs/tsconfig.json +26 -0
  118. package/packages/errors/solidjs/tsup.config.ts +21 -0
  119. package/packages/errors/src/index.ts +320 -0
  120. package/packages/errors/tsup.config.ts +12 -0
  121. package/packages/logger/package.json +27 -0
  122. package/packages/logger/react/README.md +1 -0
  123. package/packages/logger/react/package.json +46 -0
  124. package/packages/logger/react/src/index.ts +4 -0
  125. package/packages/logger/react/src/useMetrics.ts +42 -0
  126. package/packages/logger/react/src/usePerformanceLog.ts +61 -0
  127. package/packages/logger/react/tsconfig.json +31 -0
  128. package/packages/logger/react/tsup.config.ts +12 -0
  129. package/packages/logger/solidjs/README.md +1 -0
  130. package/packages/logger/solidjs/package.json +45 -0
  131. package/packages/logger/solidjs/src/createMetrics.ts +37 -0
  132. package/packages/logger/solidjs/src/createPerformanceLog.ts +58 -0
  133. package/packages/logger/solidjs/src/index.ts +4 -0
  134. package/packages/logger/solidjs/tsconfig.json +32 -0
  135. package/packages/logger/solidjs/tsup.config.ts +12 -0
  136. package/packages/logger/src/index.ts +363 -0
  137. package/packages/logger/tsup.config.ts +12 -0
  138. package/packages/perf/package.json +27 -0
  139. package/packages/perf/react/README.md +1 -0
  140. package/packages/perf/react/package.json +59 -0
  141. package/packages/perf/react/src/components/PerformanceDashboard.tsx +257 -0
  142. package/packages/perf/react/src/hooks/useMonitoredQuery.ts +89 -0
  143. package/packages/perf/react/src/hooks/usePerformanceMetrics.ts +78 -0
  144. package/packages/perf/react/src/index.ts +33 -0
  145. package/packages/perf/react/src/services/PerformanceMonitor.ts +313 -0
  146. package/packages/perf/react/src/types.ts +77 -0
  147. package/packages/perf/react/tsconfig.json +25 -0
  148. package/packages/perf/react/tsup.config.ts +19 -0
  149. package/packages/perf/solidjs/README.md +1 -0
  150. package/packages/perf/solidjs/package.json +41 -0
  151. package/packages/perf/solidjs/src/components/PerformanceDashboard.tsx +207 -0
  152. package/packages/perf/solidjs/src/hooks/createPerformanceMetrics.ts +73 -0
  153. package/packages/perf/solidjs/src/index.ts +31 -0
  154. package/packages/perf/solidjs/src/services/PerformanceMonitor.ts +134 -0
  155. package/packages/perf/solidjs/src/types.ts +78 -0
  156. package/packages/perf/solidjs/tsconfig.json +26 -0
  157. package/packages/perf/solidjs/tsup.config.ts +14 -0
  158. package/packages/perf/src/index.ts +410 -0
  159. package/packages/perf/tsup.config.ts +12 -0
  160. package/pnpm-workspace.yaml +2 -0
@@ -0,0 +1,58 @@
1
+ // @geenius-tools/convex-wrappers — src/utils.ts
2
+
3
+ /**
4
+ * Generate a unique request ID for tracing.
5
+ */
6
+ export function makeRequestId(): string {
7
+ const timestamp = Date.now().toString(36)
8
+ const random = Math.random().toString(36).substring(2, 8)
9
+ return `req_${timestamp}_${random}`
10
+ }
11
+
12
+ /**
13
+ * Redact sensitive fields from an object for logging.
14
+ */
15
+ export function redactForLogging(
16
+ args: Record<string, unknown>,
17
+ config: { sensitiveFields?: string[] },
18
+ ): Record<string, unknown> {
19
+ const sensitiveFields = new Set(
20
+ config.sensitiveFields ?? ['password', 'token', 'secret', 'apiKey', 'accessToken'],
21
+ )
22
+
23
+ const result: Record<string, unknown> = {}
24
+ for (const [key, value] of Object.entries(args)) {
25
+ if (sensitiveFields.has(key)) {
26
+ result[key] = '[REDACTED]'
27
+ } else {
28
+ result[key] = value
29
+ }
30
+ }
31
+ return result
32
+ }
33
+
34
+ /**
35
+ * Extract userId from args (for trusted internal calls).
36
+ * Throws if userId is missing.
37
+ */
38
+ export function extractRequiredUserId(
39
+ args: Record<string, unknown>,
40
+ context: string,
41
+ ): string {
42
+ const userId = args.userId
43
+ if (!userId || typeof userId !== 'string') {
44
+ throw new Error(`Missing userId in ${context}`)
45
+ }
46
+ return userId
47
+ }
48
+
49
+ /**
50
+ * Extract optional userId from args.
51
+ */
52
+ export function extractOptionalUserId(
53
+ args: Record<string, unknown>,
54
+ ): string | undefined {
55
+ const userId = args.userId
56
+ if (!userId || typeof userId !== 'string') return undefined
57
+ return userId
58
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": [
7
+ "ES2022"
8
+ ],
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "declaration": true,
16
+ "declarationMap": true,
17
+ "sourceMap": true,
18
+ "outDir": "./dist",
19
+ "rootDir": "./src"
20
+ },
21
+ "include": [
22
+ "src"
23
+ ],
24
+ "exclude": [
25
+ "node_modules",
26
+ "dist"
27
+ ]
28
+ }
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: { index: 'src/index.ts' },
5
+ outDir: 'dist',
6
+ format: ['esm'],
7
+ dts: true,
8
+ sourcemap: true,
9
+ clean: true,
10
+ treeshake: true,
11
+ external: ['convex'],
12
+ })
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@geenius-tools/devtools",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Development tooling utilities for Geenius projects",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "type-check": "tsc --noEmit",
17
+ "clean": "rm -rf dist .turbo"
18
+ },
19
+ "devDependencies": {
20
+ "typescript": "~6.0.2",
21
+ "tsup": "^8.0.1"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "license": "MIT"
27
+ }
@@ -0,0 +1 @@
1
+ # ✦ @geenius-tools/devtools-react\n\n> React dev-tools UI — floating toolbar, performance panel, inspector, GitHub issues\n\n---\n\n## Overview\nBuilt with Steve Jobs-level minimalism and Jony Ive-level craftsmanship, this package is designed to deliver unparalleled developer experience (DX) and rock-solid performance.\n\n## Installation\n\n```bash\npnpm add @geenius-tools/devtools-react\n```\n\n## Usage\n\n```typescript\nimport { init } from '@geenius-tools/devtools-react';\n\n// Initialize the module with absolute precision\ninit({\n mode: 'premium',\n});\n```\n\n## Architecture\n- **Zero-config**: It just works.\n- **Strictly Typed**: Fully written in TypeScript for flawless IntelliSense.\n- **Framework Agnostic**: seamlessly integrates into the Geenius ecosystem.\n\n---\n\n*Designed by Antigravity HQ*\n
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@geenius-tools/devtools-react",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "React dev-tools UI — floating toolbar, performance panel, inspector, GitHub issues",
7
+ "license": "MIT",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "src"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "clean": "rm -rf dist",
27
+ "type-check": "tsc --noEmit",
28
+ "prepublishOnly": "pnpm clean && pnpm build"
29
+ },
30
+ "dependencies": {
31
+ "@geenius-tools/logger": "workspace:*"
32
+ },
33
+ "devDependencies": {
34
+ "@geenius-ui/react": "workspace:*",
35
+ "@types/react": "^19.0.0",
36
+ "@types/react-dom": "^19.0.0",
37
+ "lucide-react": "^0.577.0",
38
+ "react": "^19.2.4",
39
+ "react-dom": "^19.2.4",
40
+ "tsup": "^8.5.1",
41
+ "typescript": "~5.9.3"
42
+ },
43
+ "peerDependencies": {
44
+ "@geenius-ui/react": "workspace:*",
45
+ "lucide-react": ">=0.300.0",
46
+ "react": "^18.0.0 || ^19.0.0",
47
+ "react-dom": "^18.0.0 || ^19.0.0"
48
+ },
49
+ "author": "Antigravity HQ",
50
+ "engines": {
51
+ "node": ">=20.0.0"
52
+ }
53
+ }
@@ -0,0 +1,59 @@
1
+ // @geenius-tools/devtools-react — components/DesignPreview.tsx
2
+ import { useRef, useEffect } from 'react'
3
+ import type { Design } from '../data/designs'
4
+
5
+ export type DesignPreviewProps = {
6
+ designNumber: number
7
+ showGrid?: boolean
8
+ theme?: 'light' | 'dark'
9
+ }
10
+
11
+ /**
12
+ * DesignPreview — renders an HTML design file in an iframe with grid overlay + theme control.
13
+ *
14
+ * Uses Tailwind CSS utility classes.
15
+ */
16
+ export function DesignPreview({
17
+ designNumber,
18
+ showGrid = false,
19
+ theme = 'light',
20
+ }: DesignPreviewProps) {
21
+ const iframeRef = useRef<HTMLIFrameElement>(null)
22
+
23
+ const applyTheme = () => {
24
+ try {
25
+ const iframe = iframeRef.current
26
+ if (iframe?.contentDocument) {
27
+ const action = theme === 'dark' ? 'add' : 'remove'
28
+ iframe.contentDocument.documentElement.classList[action]('dark')
29
+ iframe.contentDocument.body?.classList[action]('dark')
30
+ }
31
+ } catch {
32
+ // Cross-origin restrictions
33
+ }
34
+ }
35
+
36
+ useEffect(applyTheme, [theme, designNumber])
37
+
38
+ return (
39
+ <div className="relative w-full h-screen">
40
+ {showGrid && (
41
+ <div className="absolute inset-0 pointer-events-none z-[100] flex justify-between px-4 max-w-[1400px] mx-auto w-full">
42
+ {Array.from({ length: 12 }).map((_, i) => (
43
+ <div
44
+ key={i}
45
+ className="h-full w-[calc(100%/12)] border-x border-red-500/20 bg-red-500/5 mx-2"
46
+ />
47
+ ))}
48
+ </div>
49
+ )}
50
+ <iframe
51
+ ref={iframeRef}
52
+ src={`/designs/design-${designNumber}.html`}
53
+ className="w-full h-full border-0"
54
+ title={`Design ${designNumber} Preview`}
55
+ onLoad={applyTheme}
56
+ />
57
+ </div>
58
+ )
59
+ }
@@ -0,0 +1,99 @@
1
+ // @geenius-tools/devtools-react — components/DesignSwitcherDropdown.tsx
2
+ import { useState, useRef, useEffect } from 'react'
3
+
4
+ export type DesignSwitcherDropdownProps = {
5
+ /** Current active path — used to highlight the active design */
6
+ currentPath: string
7
+ /** Called when user selects a design */
8
+ onNavigate: (path: string) => void
9
+ /** Total number of designs */
10
+ designCount?: number
11
+ }
12
+
13
+ /**
14
+ * DesignSwitcherDropdown — dropdown menu for switching between design previews.
15
+ *
16
+ * Router-agnostic: caller provides currentPath and onNavigate.
17
+ * Uses Tailwind CSS utility classes.
18
+ */
19
+ export function DesignSwitcherDropdown({
20
+ currentPath,
21
+ onNavigate,
22
+ designCount = 21,
23
+ }: DesignSwitcherDropdownProps) {
24
+ const [isOpen, setIsOpen] = useState(false)
25
+ const dropdownRef = useRef<HTMLDivElement>(null)
26
+
27
+ const getCurrentDesign = () => {
28
+ const match = currentPath.match(/\/dev\/design\/(\d+)/)
29
+ return match ? `Design ${match[1]}` : 'Current Design'
30
+ }
31
+
32
+ const currentDesign = getCurrentDesign()
33
+
34
+ useEffect(() => {
35
+ const handleClickOutside = (event: MouseEvent) => {
36
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
37
+ setIsOpen(false)
38
+ }
39
+ }
40
+ document.addEventListener('mousedown', handleClickOutside)
41
+ return () => document.removeEventListener('mousedown', handleClickOutside)
42
+ }, [])
43
+
44
+ const handleDesignChange = (path: string) => {
45
+ onNavigate(path)
46
+ setIsOpen(false)
47
+ }
48
+
49
+ const designs = [
50
+ { label: 'Current Design', path: '/' },
51
+ ...Array.from({ length: designCount }, (_, i) => ({
52
+ label: `Design ${i + 1}`,
53
+ path: `/dev/design/${i + 1}`,
54
+ })),
55
+ ]
56
+
57
+ return (
58
+ <div ref={dropdownRef} className="relative">
59
+ <button
60
+ type="button"
61
+ onClick={() => setIsOpen(!isOpen)}
62
+ aria-label="Switch Design"
63
+ className="flex items-center gap-2 px-3 py-2 rounded-lg text-foreground hover:bg-bg-muted transition-colors text-sm font-medium"
64
+ >
65
+ <svg className="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}><circle cx="13.5" cy="6.5" r="0.5" /><circle cx="17.5" cy="10.5" r="0.5" /><circle cx="8.5" cy="7.5" r="0.5" /><circle cx="6.5" cy="12" r="0.5" /><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z" /></svg>
66
+ <span className="hidden sm:inline">{currentDesign}</span>
67
+ <svg
68
+ className={`w-4 h-4 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
69
+ fill="none"
70
+ stroke="currentColor"
71
+ viewBox="0 0 24 24"
72
+ >
73
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
74
+ </svg>
75
+ </button>
76
+
77
+ {isOpen && (
78
+ <div className="absolute right-0 mt-2 py-2 bg-card border border-border rounded-lg shadow-xl overflow-hidden min-w-[180px] max-h-[400px] overflow-y-auto z-50">
79
+ {designs.map((design) => (
80
+ <button
81
+ key={design.path}
82
+ type="button"
83
+ onClick={() => handleDesignChange(design.path)}
84
+ className={`w-full px-4 py-2.5 text-left text-sm transition-colors ${currentDesign === design.label
85
+ ? 'bg-primary/10 text-primary font-medium'
86
+ : 'hover:bg-bg-muted text-foreground'
87
+ }`}
88
+ >
89
+ {design.label}
90
+ {currentDesign === design.label && (
91
+ <span className="ml-2 text-primary">✓</span>
92
+ )}
93
+ </button>
94
+ ))}
95
+ </div>
96
+ )}
97
+ </div>
98
+ )
99
+ }
@@ -0,0 +1,247 @@
1
+ // @geenius-tools/devtools-react — src/components/DevSidebar.tsx
2
+
3
+ import { memo, useState, useEffect, useMemo } from 'react'
4
+ import { cn } from '@geenius-ui/react'
5
+ import {
6
+ Bug,
7
+ Lightbulb,
8
+ ExternalLink,
9
+ Clock,
10
+ X,
11
+ RefreshCw,
12
+ Maximize2,
13
+ Minimize2,
14
+ } from 'lucide-react'
15
+ import { useDevModeOptional } 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 = useMemo(() => {
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
+ className={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 className="flex items-start gap-3">
53
+ <div
54
+ className={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 className="w-4 h-4" />
63
+ ) : (
64
+ <Lightbulb className="w-4 h-4" />
65
+ )}
66
+ </div>
67
+
68
+ <div className="flex-1 min-w-0">
69
+ <p className="font-medium text-sm text-white line-clamp-2">
70
+ {issue.title}
71
+ </p>
72
+
73
+ {linkedComponent && (
74
+ <span className="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 className="flex items-center gap-3 mt-2 text-xs text-white/40">
80
+ <span>#{issue.number}</span>
81
+ <span className="flex items-center gap-1">
82
+ <Clock className="w-3 h-3" />
83
+ {formatRelativeTime(issue.updated_at)}
84
+ </span>
85
+ </div>
86
+
87
+ {expandedView && issue.body && (
88
+ <div className="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 className="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 = useDevModeOptional()
102
+ const [issues, setIssues] = useState<GitHubIssueListItem[]>([])
103
+ const [loading, setLoading] = useState(false)
104
+ const [tab, setTab] = useState<'bugs' | 'features'>('bugs')
105
+ const [isExpanded, setIsExpanded] = useState(false)
106
+
107
+ const isSidebarOpen = devMode?.isSidebarOpen ?? false
108
+ const setSidebarOpen = devMode?.setSidebarOpen ?? (() => { })
109
+
110
+ useEffect(() => {
111
+ if (isSidebarOpen && issues.length === 0 && !loading) {
112
+ fetchIssues()
113
+ }
114
+ }, [isSidebarOpen])
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 } = useMemo(() => {
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
+ }, [issues])
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
+ className={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 className="flex items-center justify-between px-4 py-3 border-b border-white/10">
166
+ <div className="flex items-center gap-2">
167
+ <Bug className="w-4 h-4 text-primary" />
168
+ <span className="text-sm font-bold text-white">GitHub Issues</span>
169
+ <span className="text-xs text-white/40">
170
+ ({config.owner}/{config.repo})
171
+ </span>
172
+ </div>
173
+ <div className="flex items-center gap-1">
174
+ <button
175
+ onClick={handleRefresh}
176
+ disabled={loading}
177
+ className={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 className="w-3.5 h-3.5" />
183
+ </button>
184
+ <button
185
+ onClick={toggleExpand}
186
+ className="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 className="w-3.5 h-3.5" />
190
+ ) : (
191
+ <Maximize2 className="w-3.5 h-3.5" />
192
+ )}
193
+ </button>
194
+ <button
195
+ onClick={() => setSidebarOpen(false)}
196
+ className="p-1.5 text-white/40 hover:text-white hover:bg-white/10 rounded-lg transition-colors"
197
+ >
198
+ <X className="w-4 h-4" />
199
+ </button>
200
+ </div>
201
+ </div>
202
+
203
+ {/* Tabs */}
204
+ <div className="flex border-b border-white/10">
205
+ <button
206
+ onClick={() => setTab('bugs')}
207
+ className={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
+ className={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 className="flex-1 overflow-y-auto p-3 space-y-2">
231
+ {loading ? (
232
+ <div className="flex items-center justify-center py-12">
233
+ <div className="w-6 h-6 border-2 border-white/20 border-t-primary rounded-full animate-spin" />
234
+ </div>
235
+ ) : currentIssues.length === 0 ? (
236
+ <div className="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
+ })