polen 0.10.0-next.12 → 0.10.0-next.14

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 (141) hide show
  1. package/build/api/vite/plugins/build.d.ts.map +1 -1
  2. package/build/api/vite/plugins/build.js +11 -3
  3. package/build/api/vite/plugins/build.js.map +1 -1
  4. package/build/api/vite/plugins/core.d.ts.map +1 -1
  5. package/build/api/vite/plugins/core.js +12 -10
  6. package/build/api/vite/plugins/core.js.map +1 -1
  7. package/build/api/vite/plugins/pages.d.ts.map +1 -1
  8. package/build/api/vite/plugins/pages.js +6 -7
  9. package/build/api/vite/plugins/pages.js.map +1 -1
  10. package/build/api/vite/plugins/serve.d.ts.map +1 -1
  11. package/build/api/vite/plugins/serve.js +47 -7
  12. package/build/api/vite/plugins/serve.js.map +1 -1
  13. package/build/lib/file-router/diagnostic-reporter.js +2 -2
  14. package/build/lib/file-router/diagnostic-reporter.js.map +1 -1
  15. package/build/lib/graphql-document/components/CopyButton.d.ts +19 -0
  16. package/build/lib/graphql-document/components/CopyButton.d.ts.map +1 -0
  17. package/build/lib/graphql-document/components/CopyButton.js +43 -0
  18. package/build/lib/graphql-document/components/CopyButton.js.map +1 -0
  19. package/build/lib/graphql-document/components/GraphQLDocument.d.ts +0 -4
  20. package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
  21. package/build/lib/graphql-document/components/GraphQLDocument.js +52 -83
  22. package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
  23. package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts +33 -0
  24. package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts.map +1 -0
  25. package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js +48 -0
  26. package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js.map +1 -0
  27. package/build/lib/graphql-document/components/IdentifierLink.d.ts +15 -13
  28. package/build/lib/graphql-document/components/IdentifierLink.d.ts.map +1 -1
  29. package/build/lib/graphql-document/components/IdentifierLink.js +51 -117
  30. package/build/lib/graphql-document/components/IdentifierLink.js.map +1 -1
  31. package/build/lib/graphql-document/components/graphql-document-styles.d.ts +5 -0
  32. package/build/lib/graphql-document/components/graphql-document-styles.d.ts.map +1 -0
  33. package/build/lib/graphql-document/components/graphql-document-styles.js +167 -0
  34. package/build/lib/graphql-document/components/graphql-document-styles.js.map +1 -0
  35. package/build/lib/graphql-document/components/index.d.ts +2 -1
  36. package/build/lib/graphql-document/components/index.d.ts.map +1 -1
  37. package/build/lib/graphql-document/components/index.js +2 -1
  38. package/build/lib/graphql-document/components/index.js.map +1 -1
  39. package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts +43 -0
  40. package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts.map +1 -0
  41. package/build/lib/graphql-document/hooks/use-tooltip-state.js +132 -0
  42. package/build/lib/graphql-document/hooks/use-tooltip-state.js.map +1 -0
  43. package/build/lib/graphql-document/positioning-simple.d.ts +0 -5
  44. package/build/lib/graphql-document/positioning-simple.d.ts.map +1 -1
  45. package/build/lib/graphql-document/positioning-simple.js +78 -90
  46. package/build/lib/graphql-document/positioning-simple.js.map +1 -1
  47. package/build/lib/kit-temp.d.ts +103 -0
  48. package/build/lib/kit-temp.d.ts.map +1 -1
  49. package/build/lib/kit-temp.js +236 -2
  50. package/build/lib/kit-temp.js.map +1 -1
  51. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts +1 -8
  52. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts.map +1 -1
  53. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js +48 -53
  54. package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js.map +1 -1
  55. package/build/package-paths.js +3 -3
  56. package/build/package-paths.js.map +1 -1
  57. package/build/template/components/Link.d.ts +1 -1
  58. package/build/template/components/Link.d.ts.map +1 -1
  59. package/build/template/components/Link.js +14 -5
  60. package/build/template/components/Link.js.map +1 -1
  61. package/build/template/components/content/GraphQLDocumentWithSchema.d.ts.map +1 -1
  62. package/build/template/components/content/GraphQLDocumentWithSchema.js +0 -3
  63. package/build/template/components/content/GraphQLDocumentWithSchema.js.map +1 -1
  64. package/build/template/components/content/GraphQLDocumentWrapper.d.ts.map +1 -1
  65. package/build/template/components/content/GraphQLDocumentWrapper.js +8 -7
  66. package/build/template/components/content/GraphQLDocumentWrapper.js.map +1 -1
  67. package/build/template/components/sidebar/SidebarItem.js +2 -2
  68. package/build/template/entry.client.d.ts.map +1 -1
  69. package/build/template/entry.client.js +0 -3
  70. package/build/template/entry.client.js.map +1 -1
  71. package/build/template/hooks/useClientOnly.d.ts +9 -0
  72. package/build/template/hooks/useClientOnly.d.ts.map +1 -0
  73. package/build/template/hooks/useClientOnly.js +16 -0
  74. package/build/template/hooks/useClientOnly.js.map +1 -0
  75. package/build/template/routes/root.d.ts.map +1 -1
  76. package/build/template/routes/root.js +2 -150
  77. package/build/template/routes/root.js.map +1 -1
  78. package/build/template/server/app.d.ts +8 -1
  79. package/build/template/server/app.d.ts.map +1 -1
  80. package/build/template/server/app.js +21 -21
  81. package/build/template/server/app.js.map +1 -1
  82. package/build/template/server/create-page-html-response.d.ts +7 -0
  83. package/build/template/server/create-page-html-response.d.ts.map +1 -0
  84. package/build/template/server/{render-page.js → create-page-html-response.js} +11 -16
  85. package/build/template/server/create-page-html-response.js.map +1 -0
  86. package/build/template/server/main.js +2 -1
  87. package/build/template/server/main.js.map +1 -1
  88. package/build/template/server/middleware/page.d.ts +4 -0
  89. package/build/template/server/middleware/page.d.ts.map +1 -0
  90. package/build/template/server/middleware/page.js +15 -0
  91. package/build/template/server/middleware/page.js.map +1 -0
  92. package/build/template/server/middleware/unsupported-assets.d.ts +10 -0
  93. package/build/template/server/middleware/unsupported-assets.d.ts.map +1 -0
  94. package/build/template/server/middleware/unsupported-assets.js +21 -0
  95. package/build/template/server/middleware/unsupported-assets.js.map +1 -0
  96. package/build/template/server/ssg/generate.d.ts.map +1 -1
  97. package/build/template/server/ssg/generate.js +33 -34
  98. package/build/template/server/ssg/generate.js.map +1 -1
  99. package/build/template/styles/code-block.css +218 -0
  100. package/package.json +4 -2
  101. package/src/api/singletons/markdown/markdown.test.ts +1 -1
  102. package/src/api/vite/plugins/build.ts +97 -89
  103. package/src/api/vite/plugins/core.ts +15 -10
  104. package/src/api/vite/plugins/pages.ts +9 -7
  105. package/src/api/vite/plugins/serve.ts +62 -9
  106. package/src/lib/file-router/diagnostic-reporter.ts +2 -2
  107. package/src/lib/graphql-document/components/CopyButton.tsx +76 -0
  108. package/src/lib/graphql-document/components/GraphQLDocument.tsx +73 -95
  109. package/src/lib/graphql-document/components/GraphQLIdentifierPopover.tsx +197 -0
  110. package/src/lib/graphql-document/components/IdentifierLink.tsx +105 -166
  111. package/src/lib/graphql-document/components/graphql-document-styles.ts +167 -0
  112. package/src/lib/graphql-document/components/index.ts +2 -1
  113. package/src/lib/graphql-document/hooks/use-tooltip-state.test.ts +76 -0
  114. package/src/lib/graphql-document/hooks/use-tooltip-state.ts +191 -0
  115. package/src/lib/graphql-document/positioning-simple.test.ts +18 -22
  116. package/src/lib/graphql-document/positioning-simple.ts +97 -108
  117. package/src/lib/kit-temp.test.ts +15 -3
  118. package/src/lib/kit-temp.ts +304 -4
  119. package/src/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.ts +52 -58
  120. package/src/package-paths.ts +3 -3
  121. package/src/template/components/Link.tsx +20 -12
  122. package/src/template/components/content/GraphQLDocumentWithSchema.tsx +0 -5
  123. package/src/template/components/content/GraphQLDocumentWrapper.tsx +14 -7
  124. package/src/template/components/sidebar/SidebarItem.tsx +2 -2
  125. package/src/template/entry.client.tsx +0 -3
  126. package/src/template/hooks/useClientOnly.ts +21 -0
  127. package/src/template/routes/root.tsx +0 -159
  128. package/src/template/server/app.ts +33 -23
  129. package/src/template/server/{render-page.tsx → create-page-html-response.ts} +19 -16
  130. package/src/template/server/main.ts +2 -1
  131. package/src/template/server/middleware/page.ts +19 -0
  132. package/src/template/server/middleware/unsupported-assets.ts +25 -0
  133. package/src/template/server/ssg/generate.ts +68 -72
  134. package/build/lib/graphql-document/components/HoverTooltip.d.ts +0 -35
  135. package/build/lib/graphql-document/components/HoverTooltip.d.ts.map +0 -1
  136. package/build/lib/graphql-document/components/HoverTooltip.js +0 -132
  137. package/build/lib/graphql-document/components/HoverTooltip.js.map +0 -1
  138. package/build/template/server/render-page.d.ts +0 -3
  139. package/build/template/server/render-page.d.ts.map +0 -1
  140. package/build/template/server/render-page.js.map +0 -1
  141. package/src/lib/graphql-document/components/HoverTooltip.tsx +0 -282
@@ -1,14 +1,15 @@
1
1
  import type { React } from '#dep/react/index'
2
+ import { React as ReactHooks } from '#dep/react/index'
2
3
  import type { GraphQLSchema } from 'graphql'
3
- import { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { useNavigate } from 'react-router'
5
5
  import { analyze } from '../analysis.ts'
6
+ import { useTooltipState } from '../hooks/use-tooltip-state.ts'
6
7
  import { createSimplePositionCalculator } from '../positioning-simple.ts'
7
8
  import { createPolenSchemaResolver } from '../schema-integration.ts'
8
9
  import type { Identifier } from '../types.ts'
9
- import { hoverTooltipStyles } from './HoverTooltip.tsx'
10
+ import { CopyButton } from './CopyButton.tsx'
11
+ import { graphqlDocumentStyles } from './graphql-document-styles.ts'
10
12
  import { IdentifierLink } from './IdentifierLink.tsx'
11
- import { identifierLinkStyles } from './IdentifierLink.tsx'
12
13
 
13
14
  /**
14
15
  * Options for the GraphQL document component
@@ -64,52 +65,45 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
64
65
  const handleNavigate = onNavigate || ((url: string) => navigate(url))
65
66
 
66
67
  // Container ref for positioning calculations
67
- const containerRef = useRef<HTMLDivElement>(null)
68
- const [isReady, setIsReady] = useState(false)
69
- const [openTooltipId, setOpenTooltipId] = useState<string | null>(null)
70
-
71
- // Handle click outside to close tooltips
72
- useEffect(() => {
73
- if (!openTooltipId) return
74
-
75
- const handleClickOutside = (event: MouseEvent) => {
76
- // Check if click is outside the tooltip and identifier links
77
- const target = event.target as HTMLElement
78
-
79
- // Don't close if clicking on an identifier link or tooltip
80
- if (
81
- target.closest('.graphql-identifier-overlay')
82
- || target.closest('.graphql-hover-tooltip')
83
- ) {
84
- return
68
+ const containerRef = ReactHooks.useRef<HTMLDivElement>(null)
69
+ const [isReady, setIsReady] = ReactHooks.useState(false)
70
+
71
+ // Use tooltip state management
72
+ const tooltipState = useTooltipState({
73
+ showDelay: 300,
74
+ hideDelay: 200, // Increased for smoother experience
75
+ allowMultiplePins: true,
76
+ })
77
+
78
+ // Handle escape key to unpin all
79
+ ReactHooks.useEffect(() => {
80
+ const handleKeyDown = (event: KeyboardEvent) => {
81
+ if (event.key === 'Escape') {
82
+ tooltipState.unpinAll()
85
83
  }
86
-
87
- // Close the tooltip
88
- setOpenTooltipId(null)
89
84
  }
90
85
 
91
- // Add event listener
92
- document.addEventListener('mousedown', handleClickOutside)
93
-
86
+ document.addEventListener('keydown', handleKeyDown)
94
87
  return () => {
95
- document.removeEventListener('mousedown', handleClickOutside)
88
+ document.removeEventListener('keydown', handleKeyDown)
96
89
  }
97
- }, [openTooltipId])
90
+ }, [tooltipState])
98
91
 
99
92
  // Layer 1: Parse and analyze
100
- const analysisResult = useMemo(() => {
93
+ const analysisResult = ReactHooks.useMemo(() => {
101
94
  if (plain) return null
102
95
  const result = analyze(children, { schema })
96
+ // Debug logging handled by debug prop
103
97
  return result
104
- }, [children, plain, schema])
98
+ }, [children, plain, schema, debug])
105
99
 
106
100
  // Layer 2: Schema resolution
107
- const resolver = useMemo(() => {
101
+ const resolver = ReactHooks.useMemo(() => {
108
102
  if (!schema || plain) return null
109
103
  return createPolenSchemaResolver(schema)
110
104
  }, [schema, plain])
111
105
 
112
- const resolutions = useMemo(() => {
106
+ const resolutions = ReactHooks.useMemo(() => {
113
107
  if (!analysisResult || !resolver) {
114
108
  return new Map()
115
109
  }
@@ -125,16 +119,19 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
125
119
  }, [analysisResult, resolver])
126
120
 
127
121
  // Layer 3: Position calculation
128
- const positionCalculator = useMemo(() => {
122
+ const positionCalculator = ReactHooks.useMemo(() => {
129
123
  if (plain) return null
130
124
  return createSimplePositionCalculator()
131
125
  }, [plain])
132
126
 
133
- const [positions, setPositions] = useState<Map<string, { position: any; identifier: Identifier }>>(new Map())
127
+ const [positions, setPositions] = ReactHooks.useState<Map<string, { position: any; identifier: Identifier }>>(
128
+ new Map(),
129
+ )
134
130
 
135
131
  // Prepare code block and calculate positions after render
136
- useEffect(() => {
132
+ ReactHooks.useEffect(() => {
137
133
  if (!containerRef.current || !analysisResult || !positionCalculator || plain) {
134
+ // Skip position calculation - debug handled by debug prop
138
135
  return
139
136
  }
140
137
 
@@ -143,11 +140,13 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
143
140
  || containerRef.current.querySelector('pre code')
144
141
  || containerRef.current.querySelector('code')
145
142
  if (!codeElement) {
143
+ // No code element found - skip
146
144
  return
147
145
  }
148
146
 
149
147
  // Prepare the code block (wrap identifiers)
150
148
  const identifiers = Array.from(analysisResult.identifiers.byPosition.values())
149
+ // Prepare code block with identifiers
151
150
  positionCalculator.prepareCodeBlock(codeElement as Element, identifiers)
152
151
 
153
152
  // Get positions after DOM update
@@ -155,45 +154,52 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
155
154
  // Pass containerRef.current as the reference element for positioning
156
155
  if (containerRef.current) {
157
156
  const newPositions = positionCalculator.getIdentifierPositions(codeElement as Element, containerRef.current)
157
+ // Position calculation complete
158
158
  setPositions(newPositions)
159
159
  setIsReady(true)
160
160
  }
161
161
  })
162
162
  }, [analysisResult, positionCalculator, plain, highlightedHtml])
163
163
 
164
- // Handle resize events
165
- useEffect(() => {
164
+ // Handle resize events with debouncing
165
+ ReactHooks.useEffect(() => {
166
166
  if (!containerRef.current || !positionCalculator || plain) return
167
167
 
168
+ let resizeTimer: NodeJS.Timeout
168
169
  const handleResize = () => {
169
- const codeElement = containerRef.current?.querySelector('pre.shiki code')
170
- || containerRef.current?.querySelector('pre code')
171
- || containerRef.current?.querySelector('code')
172
- if (codeElement && containerRef.current) {
173
- const newPositions = positionCalculator.getIdentifierPositions(codeElement as Element, containerRef.current)
174
- setPositions(newPositions)
175
- }
170
+ clearTimeout(resizeTimer)
171
+ resizeTimer = setTimeout(() => {
172
+ const codeElement = containerRef.current?.querySelector('pre.shiki code')
173
+ || containerRef.current?.querySelector('pre code')
174
+ || containerRef.current?.querySelector('code')
175
+ if (codeElement && containerRef.current) {
176
+ const newPositions = positionCalculator.getIdentifierPositions(codeElement as Element, containerRef.current)
177
+ setPositions(newPositions)
178
+ }
179
+ }, 100) // Debounce resize events
176
180
  }
177
181
 
178
182
  window.addEventListener('resize', handleResize)
179
- return () => window.removeEventListener('resize', handleResize)
183
+ return () => {
184
+ clearTimeout(resizeTimer)
185
+ window.removeEventListener('resize', handleResize)
186
+ }
180
187
  }, [positionCalculator, plain])
181
188
 
182
189
  // Validation errors
183
- const validationErrors = useMemo(() => {
190
+ const validationErrors = ReactHooks.useMemo(() => {
184
191
  if (!validate || !analysisResult || !schema) return []
185
192
  return analysisResult.errors
186
193
  }, [validate, analysisResult, schema])
187
194
 
188
195
  return (
189
196
  <>
190
- {/* Inject styles */}
191
- <style dangerouslySetInnerHTML={{ __html: identifierLinkStyles + '\n' + hoverTooltipStyles }} />
192
-
197
+ <style dangerouslySetInnerHTML={{ __html: graphqlDocumentStyles }} />
193
198
  <div
194
199
  ref={containerRef}
195
- className={`graphql-document ${className} ${debug ? 'graphql-debug-mode' : ''}`}
196
- style={{ position: 'relative' }}
200
+ className={`graphql-document ${className} ${debug ? 'graphql-debug-mode' : ''} ${
201
+ !isReady && !plain ? 'graphql-loading' : ''
202
+ }`}
197
203
  >
198
204
  {/* Base syntax highlighting */}
199
205
  {highlightedHtml ? <div dangerouslySetInnerHTML={{ __html: highlightedHtml }} /> : (
@@ -202,6 +208,15 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
202
208
  </pre>
203
209
  )}
204
210
 
211
+ {/* Copy button */}
212
+ {!plain && (
213
+ <CopyButton
214
+ text={children}
215
+ className='graphql-document-copy'
216
+ size='2'
217
+ />
218
+ )}
219
+
205
220
  {/* Interactive overlay layer */}
206
221
  {!plain && isReady && (
207
222
  <div className='graphql-interaction-layer' style={{ pointerEvents: 'none' }}>
@@ -219,8 +234,12 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
219
234
  position={position}
220
235
  onNavigate={handleNavigate}
221
236
  debug={debug}
222
- isOpen={openTooltipId === id}
223
- onToggle={(open) => setOpenTooltipId(open ? id : null)}
237
+ isOpen={tooltipState.isOpen(id)}
238
+ isPinned={tooltipState.isPinned(id)}
239
+ onHoverStart={() => tooltipState.onHoverStart(id)}
240
+ onHoverEnd={() => tooltipState.onHoverEnd(id)}
241
+ onTogglePin={() => tooltipState.onTogglePin(id)}
242
+ onTooltipHover={() => tooltipState.onTooltipHover(id)}
224
243
  />
225
244
  )
226
245
  })}
@@ -241,44 +260,3 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
241
260
  </>
242
261
  )
243
262
  }
244
-
245
- /**
246
- * Default styles for the GraphQL document component
247
- */
248
- export const graphqlDocumentStyles = `
249
- .graphql-document {
250
- position: relative;
251
- }
252
-
253
- .graphql-interaction-layer {
254
- position: absolute;
255
- top: 0;
256
- left: 0;
257
- right: 0;
258
- bottom: 0;
259
- pointer-events: none;
260
- }
261
-
262
- .graphql-interaction-layer > * {
263
- pointer-events: auto;
264
- }
265
-
266
- .graphql-validation-errors {
267
- margin-top: 1rem;
268
- padding: 0.5rem;
269
- background-color: var(--red-2);
270
- border: 1px solid var(--red-6);
271
- border-radius: 4px;
272
- }
273
-
274
- .graphql-error {
275
- color: var(--red-11);
276
- font-size: 0.875rem;
277
- margin: 0.25rem 0;
278
- }
279
-
280
- .graphql-debug-mode [data-graphql-id] {
281
- background-color: rgba(59, 130, 246, 0.1);
282
- outline: 1px solid rgba(59, 130, 246, 0.3);
283
- }
284
- `
@@ -0,0 +1,197 @@
1
+ /**
2
+ * GraphQL Identifier Popover using Radix Themes
3
+ *
4
+ * Displays rich information about GraphQL identifiers on hover/click
5
+ */
6
+
7
+ import type { React } from '#dep/react/index'
8
+ import { Cross2Icon } from '@radix-ui/react-icons'
9
+ import { Badge, Box, Card, Flex, IconButton, Link, Popover, Text } from '@radix-ui/themes'
10
+ import type { Documentation } from '../schema-integration.ts'
11
+ import type { Identifier } from '../types.ts'
12
+
13
+ export interface GraphQLIdentifierPopoverProps {
14
+ /** The identifier being shown */
15
+ identifier: Identifier
16
+ /** Documentation from schema */
17
+ documentation: Documentation
18
+ /** Whether this identifier has an error */
19
+ hasError?: boolean
20
+ /** Reference URL for "View docs" link */
21
+ referenceUrl: string
22
+ /** Whether popover is open */
23
+ open: boolean
24
+ /** Whether popover is pinned */
25
+ isPinned: boolean
26
+ /** Callback when open state changes */
27
+ onOpenChange: (open: boolean) => void
28
+ /** Callback to navigate to docs */
29
+ onNavigate?: (url: string) => void
30
+ /** The trigger element */
31
+ children: React.ReactNode
32
+ }
33
+
34
+ /**
35
+ * Popover content for GraphQL identifiers
36
+ */
37
+ export const GraphQLIdentifierPopover: React.FC<GraphQLIdentifierPopoverProps> = ({
38
+ identifier,
39
+ documentation,
40
+ hasError = false,
41
+ referenceUrl,
42
+ open,
43
+ isPinned,
44
+ onOpenChange,
45
+ onNavigate,
46
+ children,
47
+ }) => {
48
+ // Determine badge color based on identifier kind
49
+ const getBadgeColor = () => {
50
+ switch (identifier.kind) {
51
+ case 'Type':
52
+ return 'blue'
53
+ case 'Field':
54
+ return 'green'
55
+ case 'Argument':
56
+ return 'orange'
57
+ case 'Variable':
58
+ return 'purple'
59
+ case 'Directive':
60
+ return 'amber'
61
+ case 'Fragment':
62
+ return 'cyan'
63
+ default:
64
+ return 'gray'
65
+ }
66
+ }
67
+
68
+ return (
69
+ <Popover.Root open={open} onOpenChange={onOpenChange}>
70
+ <Popover.Trigger>
71
+ {children}
72
+ </Popover.Trigger>
73
+
74
+ <Popover.Content
75
+ className='graphql-identifier-popover'
76
+ style={{ maxWidth: 400 }}
77
+ onInteractOutside={(e) => {
78
+ // Prevent closing when clicking inside popover if pinned
79
+ if (isPinned) {
80
+ e.preventDefault()
81
+ }
82
+ }}
83
+ >
84
+ <Flex direction='column' gap='2'>
85
+ {/* Header with name, kind, and close button */}
86
+ <Flex justify='between' align='center'>
87
+ <Flex align='center' gap='2'>
88
+ <Text size='2' weight='bold'>
89
+ {identifier.name}
90
+ </Text>
91
+ <Badge color={getBadgeColor()} size='1'>
92
+ {identifier.kind}
93
+ </Badge>
94
+ </Flex>
95
+ {isPinned && (
96
+ <IconButton
97
+ size='1'
98
+ variant='ghost'
99
+ onClick={() => onOpenChange(false)}
100
+ aria-label='Close popover'
101
+ >
102
+ <Cross2Icon />
103
+ </IconButton>
104
+ )}
105
+ </Flex>
106
+
107
+ {/* Type signature */}
108
+ <Box>
109
+ <Text size='1' color='gray'>
110
+ Type: <Text as='span' size='1' style={{ fontFamily: 'monospace' }}>{documentation.typeInfo}</Text>
111
+ </Text>
112
+ </Box>
113
+
114
+ {/* Description */}
115
+ {documentation.description && (
116
+ <Box>
117
+ <Text size='1'>
118
+ {documentation.description}
119
+ </Text>
120
+ </Box>
121
+ )}
122
+
123
+ {/* Default value for arguments */}
124
+ {documentation.defaultValue && (
125
+ <Box>
126
+ <Text size='1' color='gray'>
127
+ Default:{' '}
128
+ <Text as='span' size='1' style={{ fontFamily: 'monospace' }}>{documentation.defaultValue}</Text>
129
+ </Text>
130
+ </Box>
131
+ )}
132
+
133
+ {/* Deprecation warning */}
134
+ {documentation.deprecated && (
135
+ <Box
136
+ style={{
137
+ padding: '8px',
138
+ backgroundColor: 'var(--amber-2)',
139
+ borderRadius: '4px',
140
+ border: '1px solid var(--amber-6)',
141
+ }}
142
+ >
143
+ <Text size='1' color='amber'>
144
+ ⚠️ Deprecated: {documentation.deprecated.reason}
145
+ </Text>
146
+ {documentation.deprecated.replacement && (
147
+ <Text size='1' color='amber'>
148
+ Use {documentation.deprecated.replacement} instead.
149
+ </Text>
150
+ )}
151
+ </Box>
152
+ )}
153
+
154
+ {/* Error message */}
155
+ {hasError && (
156
+ <Box
157
+ style={{
158
+ padding: '8px',
159
+ backgroundColor: 'var(--red-2)',
160
+ borderRadius: '4px',
161
+ border: '1px solid var(--red-6)',
162
+ }}
163
+ >
164
+ <Text size='1' color='red'>
165
+ ❌ {identifier.kind} not found in schema
166
+ </Text>
167
+ </Box>
168
+ )}
169
+
170
+ {/* Schema path */}
171
+ <Box>
172
+ <Text size='1' color='gray'>
173
+ Path: {identifier.schemaPath.join(' → ')}
174
+ </Text>
175
+ </Box>
176
+
177
+ {/* View docs link */}
178
+ {onNavigate && !hasError && (
179
+ <Box>
180
+ <Link
181
+ size='1'
182
+ href={referenceUrl}
183
+ onClick={(e: React.MouseEvent) => {
184
+ e.preventDefault()
185
+ onNavigate(referenceUrl)
186
+ onOpenChange(false)
187
+ }}
188
+ >
189
+ View full documentation →
190
+ </Link>
191
+ </Box>
192
+ )}
193
+ </Flex>
194
+ </Popover.Content>
195
+ </Popover.Root>
196
+ )
197
+ }