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.
- package/build/api/vite/plugins/build.d.ts.map +1 -1
- package/build/api/vite/plugins/build.js +11 -3
- package/build/api/vite/plugins/build.js.map +1 -1
- package/build/api/vite/plugins/core.d.ts.map +1 -1
- package/build/api/vite/plugins/core.js +12 -10
- package/build/api/vite/plugins/core.js.map +1 -1
- package/build/api/vite/plugins/pages.d.ts.map +1 -1
- package/build/api/vite/plugins/pages.js +6 -7
- package/build/api/vite/plugins/pages.js.map +1 -1
- package/build/api/vite/plugins/serve.d.ts.map +1 -1
- package/build/api/vite/plugins/serve.js +47 -7
- package/build/api/vite/plugins/serve.js.map +1 -1
- package/build/lib/file-router/diagnostic-reporter.js +2 -2
- package/build/lib/file-router/diagnostic-reporter.js.map +1 -1
- package/build/lib/graphql-document/components/CopyButton.d.ts +19 -0
- package/build/lib/graphql-document/components/CopyButton.d.ts.map +1 -0
- package/build/lib/graphql-document/components/CopyButton.js +43 -0
- package/build/lib/graphql-document/components/CopyButton.js.map +1 -0
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts +0 -4
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
- package/build/lib/graphql-document/components/GraphQLDocument.js +52 -83
- package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts +33 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.d.ts.map +1 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js +48 -0
- package/build/lib/graphql-document/components/GraphQLIdentifierPopover.js.map +1 -0
- package/build/lib/graphql-document/components/IdentifierLink.d.ts +15 -13
- package/build/lib/graphql-document/components/IdentifierLink.d.ts.map +1 -1
- package/build/lib/graphql-document/components/IdentifierLink.js +51 -117
- package/build/lib/graphql-document/components/IdentifierLink.js.map +1 -1
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts +5 -0
- package/build/lib/graphql-document/components/graphql-document-styles.d.ts.map +1 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js +167 -0
- package/build/lib/graphql-document/components/graphql-document-styles.js.map +1 -0
- package/build/lib/graphql-document/components/index.d.ts +2 -1
- package/build/lib/graphql-document/components/index.d.ts.map +1 -1
- package/build/lib/graphql-document/components/index.js +2 -1
- package/build/lib/graphql-document/components/index.js.map +1 -1
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts +43 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.d.ts.map +1 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js +132 -0
- package/build/lib/graphql-document/hooks/use-tooltip-state.js.map +1 -0
- package/build/lib/graphql-document/positioning-simple.d.ts +0 -5
- package/build/lib/graphql-document/positioning-simple.d.ts.map +1 -1
- package/build/lib/graphql-document/positioning-simple.js +78 -90
- package/build/lib/graphql-document/positioning-simple.js.map +1 -1
- package/build/lib/kit-temp.d.ts +103 -0
- package/build/lib/kit-temp.d.ts.map +1 -1
- package/build/lib/kit-temp.js +236 -2
- package/build/lib/kit-temp.js.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts +1 -8
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js +48 -53
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js.map +1 -1
- package/build/package-paths.js +3 -3
- package/build/package-paths.js.map +1 -1
- package/build/template/components/Link.d.ts +1 -1
- package/build/template/components/Link.d.ts.map +1 -1
- package/build/template/components/Link.js +14 -5
- package/build/template/components/Link.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.js +0 -3
- package/build/template/components/content/GraphQLDocumentWithSchema.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.js +8 -7
- package/build/template/components/content/GraphQLDocumentWrapper.js.map +1 -1
- package/build/template/components/sidebar/SidebarItem.js +2 -2
- package/build/template/entry.client.d.ts.map +1 -1
- package/build/template/entry.client.js +0 -3
- package/build/template/entry.client.js.map +1 -1
- package/build/template/hooks/useClientOnly.d.ts +9 -0
- package/build/template/hooks/useClientOnly.d.ts.map +1 -0
- package/build/template/hooks/useClientOnly.js +16 -0
- package/build/template/hooks/useClientOnly.js.map +1 -0
- package/build/template/routes/root.d.ts.map +1 -1
- package/build/template/routes/root.js +2 -150
- package/build/template/routes/root.js.map +1 -1
- package/build/template/server/app.d.ts +8 -1
- package/build/template/server/app.d.ts.map +1 -1
- package/build/template/server/app.js +21 -21
- package/build/template/server/app.js.map +1 -1
- package/build/template/server/create-page-html-response.d.ts +7 -0
- package/build/template/server/create-page-html-response.d.ts.map +1 -0
- package/build/template/server/{render-page.js → create-page-html-response.js} +11 -16
- package/build/template/server/create-page-html-response.js.map +1 -0
- package/build/template/server/main.js +2 -1
- package/build/template/server/main.js.map +1 -1
- package/build/template/server/middleware/page.d.ts +4 -0
- package/build/template/server/middleware/page.d.ts.map +1 -0
- package/build/template/server/middleware/page.js +15 -0
- package/build/template/server/middleware/page.js.map +1 -0
- package/build/template/server/middleware/unsupported-assets.d.ts +10 -0
- package/build/template/server/middleware/unsupported-assets.d.ts.map +1 -0
- package/build/template/server/middleware/unsupported-assets.js +21 -0
- package/build/template/server/middleware/unsupported-assets.js.map +1 -0
- package/build/template/server/ssg/generate.d.ts.map +1 -1
- package/build/template/server/ssg/generate.js +33 -34
- package/build/template/server/ssg/generate.js.map +1 -1
- package/build/template/styles/code-block.css +218 -0
- package/package.json +4 -2
- package/src/api/singletons/markdown/markdown.test.ts +1 -1
- package/src/api/vite/plugins/build.ts +97 -89
- package/src/api/vite/plugins/core.ts +15 -10
- package/src/api/vite/plugins/pages.ts +9 -7
- package/src/api/vite/plugins/serve.ts +62 -9
- package/src/lib/file-router/diagnostic-reporter.ts +2 -2
- package/src/lib/graphql-document/components/CopyButton.tsx +76 -0
- package/src/lib/graphql-document/components/GraphQLDocument.tsx +73 -95
- package/src/lib/graphql-document/components/GraphQLIdentifierPopover.tsx +197 -0
- package/src/lib/graphql-document/components/IdentifierLink.tsx +105 -166
- package/src/lib/graphql-document/components/graphql-document-styles.ts +167 -0
- package/src/lib/graphql-document/components/index.ts +2 -1
- package/src/lib/graphql-document/hooks/use-tooltip-state.test.ts +76 -0
- package/src/lib/graphql-document/hooks/use-tooltip-state.ts +191 -0
- package/src/lib/graphql-document/positioning-simple.test.ts +18 -22
- package/src/lib/graphql-document/positioning-simple.ts +97 -108
- package/src/lib/kit-temp.test.ts +15 -3
- package/src/lib/kit-temp.ts +304 -4
- package/src/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.ts +52 -58
- package/src/package-paths.ts +3 -3
- package/src/template/components/Link.tsx +20 -12
- package/src/template/components/content/GraphQLDocumentWithSchema.tsx +0 -5
- package/src/template/components/content/GraphQLDocumentWrapper.tsx +14 -7
- package/src/template/components/sidebar/SidebarItem.tsx +2 -2
- package/src/template/entry.client.tsx +0 -3
- package/src/template/hooks/useClientOnly.ts +21 -0
- package/src/template/routes/root.tsx +0 -159
- package/src/template/server/app.ts +33 -23
- package/src/template/server/{render-page.tsx → create-page-html-response.ts} +19 -16
- package/src/template/server/main.ts +2 -1
- package/src/template/server/middleware/page.ts +19 -0
- package/src/template/server/middleware/unsupported-assets.ts +25 -0
- package/src/template/server/ssg/generate.ts +68 -72
- package/build/lib/graphql-document/components/HoverTooltip.d.ts +0 -35
- package/build/lib/graphql-document/components/HoverTooltip.d.ts.map +0 -1
- package/build/lib/graphql-document/components/HoverTooltip.js +0 -132
- package/build/lib/graphql-document/components/HoverTooltip.js.map +0 -1
- package/build/template/server/render-page.d.ts +0 -3
- package/build/template/server/render-page.d.ts.map +0 -1
- package/build/template/server/render-page.js.map +0 -1
- package/src/lib/graphql-document/components/HoverTooltip.tsx +0 -282
@@ -0,0 +1,191 @@
|
|
1
|
+
/**
|
2
|
+
* State management for GraphQL document tooltips
|
3
|
+
*
|
4
|
+
* Handles hover delays, pinning, and multiple tooltip coordination
|
5
|
+
*/
|
6
|
+
|
7
|
+
import { React } from '#dep/react/index'
|
8
|
+
|
9
|
+
export interface TooltipState {
|
10
|
+
/** Currently visible tooltip (via hover) */
|
11
|
+
hoveredId: string | null
|
12
|
+
/** Set of pinned tooltip IDs */
|
13
|
+
pinnedIds: Set<string>
|
14
|
+
/** ID pending show (waiting for delay) */
|
15
|
+
pendingShowId: string | null
|
16
|
+
/** ID pending hide (waiting for grace period) */
|
17
|
+
pendingHideId: string | null
|
18
|
+
}
|
19
|
+
|
20
|
+
export interface UseTooltipStateOptions {
|
21
|
+
/** Delay before showing tooltip on hover (ms) */
|
22
|
+
showDelay?: number
|
23
|
+
/** Delay before hiding tooltip on mouse leave (ms) */
|
24
|
+
hideDelay?: number
|
25
|
+
/** Whether to allow multiple pinned tooltips */
|
26
|
+
allowMultiplePins?: boolean
|
27
|
+
}
|
28
|
+
|
29
|
+
export interface UseTooltipStateReturn {
|
30
|
+
/** Check if a tooltip should be visible */
|
31
|
+
isOpen: (id: string) => boolean
|
32
|
+
/** Check if a tooltip is pinned */
|
33
|
+
isPinned: (id: string) => boolean
|
34
|
+
/** Handle hover start */
|
35
|
+
onHoverStart: (id: string) => void
|
36
|
+
/** Handle hover end */
|
37
|
+
onHoverEnd: (id: string) => void
|
38
|
+
/** Handle click (toggle pin) */
|
39
|
+
onTogglePin: (id: string) => void
|
40
|
+
/** Handle tooltip content hover (cancels hide) */
|
41
|
+
onTooltipHover: (id: string) => void
|
42
|
+
/** Unpin a specific tooltip */
|
43
|
+
unpin: (id: string) => void
|
44
|
+
/** Unpin all tooltips */
|
45
|
+
unpinAll: () => void
|
46
|
+
}
|
47
|
+
|
48
|
+
export const useTooltipState = (options: UseTooltipStateOptions = {}): UseTooltipStateReturn => {
|
49
|
+
const {
|
50
|
+
showDelay = 300,
|
51
|
+
hideDelay = 200,
|
52
|
+
allowMultiplePins = true,
|
53
|
+
} = options
|
54
|
+
|
55
|
+
const [hoveredId, setHoveredId] = React.useState<string | null>(null)
|
56
|
+
const [pinnedIds, setPinnedIds] = React.useState<Set<string>>(new Set())
|
57
|
+
const [pendingShowId, setPendingShowId] = React.useState<string | null>(null)
|
58
|
+
const [pendingHideId, setPendingHideId] = React.useState<string | null>(null)
|
59
|
+
|
60
|
+
// Timer refs
|
61
|
+
const showTimerRef = React.useRef<NodeJS.Timeout | null>(null)
|
62
|
+
const hideTimerRef = React.useRef<NodeJS.Timeout | null>(null)
|
63
|
+
|
64
|
+
// Clear any pending timers
|
65
|
+
const clearTimers = React.useCallback(() => {
|
66
|
+
if (showTimerRef.current) {
|
67
|
+
clearTimeout(showTimerRef.current)
|
68
|
+
showTimerRef.current = null
|
69
|
+
}
|
70
|
+
if (hideTimerRef.current) {
|
71
|
+
clearTimeout(hideTimerRef.current)
|
72
|
+
hideTimerRef.current = null
|
73
|
+
}
|
74
|
+
setPendingShowId(null)
|
75
|
+
setPendingHideId(null)
|
76
|
+
}, [])
|
77
|
+
|
78
|
+
// Check if tooltip should be visible
|
79
|
+
const isOpen = React.useCallback((id: string): boolean => {
|
80
|
+
return hoveredId === id || pinnedIds.has(id)
|
81
|
+
}, [hoveredId, pinnedIds])
|
82
|
+
|
83
|
+
// Check if tooltip is pinned
|
84
|
+
const isPinned = React.useCallback((id: string): boolean => {
|
85
|
+
return pinnedIds.has(id)
|
86
|
+
}, [pinnedIds])
|
87
|
+
|
88
|
+
// Handle hover start
|
89
|
+
const onHoverStart = React.useCallback((id: string) => {
|
90
|
+
// Don't show if already pinned
|
91
|
+
if (pinnedIds.has(id)) return
|
92
|
+
|
93
|
+
// Cancel any pending hide for this ID
|
94
|
+
if (pendingHideId === id) {
|
95
|
+
clearTimeout(hideTimerRef.current!)
|
96
|
+
hideTimerRef.current = null
|
97
|
+
setPendingHideId(null)
|
98
|
+
return
|
99
|
+
}
|
100
|
+
|
101
|
+
// Clear any other pending operations
|
102
|
+
clearTimers()
|
103
|
+
|
104
|
+
// Schedule show
|
105
|
+
setPendingShowId(id)
|
106
|
+
showTimerRef.current = setTimeout(() => {
|
107
|
+
setHoveredId(id)
|
108
|
+
setPendingShowId(null)
|
109
|
+
}, showDelay)
|
110
|
+
}, [pinnedIds, pendingHideId, clearTimers, showDelay])
|
111
|
+
|
112
|
+
// Handle hover end
|
113
|
+
const onHoverEnd = React.useCallback((id: string) => {
|
114
|
+
// Don't hide if pinned
|
115
|
+
if (pinnedIds.has(id)) return
|
116
|
+
|
117
|
+
// Cancel pending show if still waiting
|
118
|
+
if (pendingShowId === id) {
|
119
|
+
clearTimeout(showTimerRef.current!)
|
120
|
+
showTimerRef.current = null
|
121
|
+
setPendingShowId(null)
|
122
|
+
return
|
123
|
+
}
|
124
|
+
|
125
|
+
// Only hide if currently showing this tooltip
|
126
|
+
if (hoveredId === id) {
|
127
|
+
setPendingHideId(id)
|
128
|
+
hideTimerRef.current = setTimeout(() => {
|
129
|
+
// First set hovered to null to trigger close animation
|
130
|
+
setHoveredId(null)
|
131
|
+
setPendingHideId(null)
|
132
|
+
}, hideDelay)
|
133
|
+
}
|
134
|
+
}, [pinnedIds, pendingShowId, hoveredId, hideDelay])
|
135
|
+
|
136
|
+
// Handle tooltip content hover (cancels hide)
|
137
|
+
const onTooltipHover = React.useCallback((id: string) => {
|
138
|
+
if (pendingHideId === id) {
|
139
|
+
clearTimeout(hideTimerRef.current!)
|
140
|
+
hideTimerRef.current = null
|
141
|
+
setPendingHideId(null)
|
142
|
+
}
|
143
|
+
}, [pendingHideId])
|
144
|
+
|
145
|
+
// Toggle pin state
|
146
|
+
const onTogglePin = React.useCallback((id: string) => {
|
147
|
+
clearTimers()
|
148
|
+
|
149
|
+
setPinnedIds((prev: Set<string>) => {
|
150
|
+
const next = new Set(prev)
|
151
|
+
if (next.has(id)) {
|
152
|
+
// Unpin
|
153
|
+
next.delete(id)
|
154
|
+
setHoveredId(null) // Also clear hover state
|
155
|
+
} else {
|
156
|
+
// Pin
|
157
|
+
if (!allowMultiplePins) {
|
158
|
+
next.clear() // Clear other pins
|
159
|
+
}
|
160
|
+
next.add(id)
|
161
|
+
setHoveredId(null) // Clear hover state since it's now pinned
|
162
|
+
}
|
163
|
+
return next
|
164
|
+
})
|
165
|
+
}, [clearTimers, allowMultiplePins])
|
166
|
+
|
167
|
+
// Unpin specific tooltip
|
168
|
+
const unpin = React.useCallback((id: string) => {
|
169
|
+
setPinnedIds((prev: Set<string>) => {
|
170
|
+
const next = new Set(prev)
|
171
|
+
next.delete(id)
|
172
|
+
return next
|
173
|
+
})
|
174
|
+
}, [])
|
175
|
+
|
176
|
+
// Unpin all tooltips
|
177
|
+
const unpinAll = React.useCallback(() => {
|
178
|
+
setPinnedIds(new Set())
|
179
|
+
}, [])
|
180
|
+
|
181
|
+
return {
|
182
|
+
isOpen,
|
183
|
+
isPinned,
|
184
|
+
onHoverStart,
|
185
|
+
onHoverEnd,
|
186
|
+
onTogglePin,
|
187
|
+
onTooltipHover,
|
188
|
+
unpin,
|
189
|
+
unpinAll,
|
190
|
+
}
|
191
|
+
}
|
@@ -33,13 +33,11 @@ describe('Simple Positioning Engine', () => {
|
|
33
33
|
const container = document.createElement('div')
|
34
34
|
container.innerHTML = `
|
35
35
|
<pre class="shiki">
|
36
|
-
<code>
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
<span class="line">}</span>
|
42
|
-
</code>
|
36
|
+
<code>query GetUser {
|
37
|
+
user {
|
38
|
+
name
|
39
|
+
}
|
40
|
+
}</code>
|
43
41
|
</pre>
|
44
42
|
`
|
45
43
|
|
@@ -49,7 +47,8 @@ describe('Simple Positioning Engine', () => {
|
|
49
47
|
createTestIdentifier('name', 3, 5, 'Field'),
|
50
48
|
]
|
51
49
|
|
52
|
-
|
50
|
+
const codeElement = container.querySelector('code')!
|
51
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
53
52
|
|
54
53
|
// Check that identifiers were wrapped
|
55
54
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -66,9 +65,7 @@ describe('Simple Positioning Engine', () => {
|
|
66
65
|
const container = document.createElement('div')
|
67
66
|
container.innerHTML = `
|
68
67
|
<pre class="shiki">
|
69
|
-
<code>
|
70
|
-
<span class="line">query GetUserById($id: ID!) {</span>
|
71
|
-
</code>
|
68
|
+
<code>query GetUserById($id: ID!) {</code>
|
72
69
|
</pre>
|
73
70
|
`
|
74
71
|
|
@@ -79,7 +76,8 @@ describe('Simple Positioning Engine', () => {
|
|
79
76
|
createTestIdentifier('ID', 1, 24, 'Type'),
|
80
77
|
]
|
81
78
|
|
82
|
-
|
79
|
+
const codeElement = container.querySelector('code')!
|
80
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
83
81
|
|
84
82
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
85
83
|
expect(wrappedElements.length).toBe(4)
|
@@ -140,9 +138,7 @@ describe('Simple Positioning Engine', () => {
|
|
140
138
|
const container = document.createElement('div')
|
141
139
|
container.innerHTML = `
|
142
140
|
<pre class="shiki">
|
143
|
-
<code>
|
144
|
-
<span class="line"><span data-graphql-id="existing">user</span> {</span>
|
145
|
-
</code>
|
141
|
+
<code><span data-graphql-id="existing">user</span> {</code>
|
146
142
|
</pre>
|
147
143
|
`
|
148
144
|
|
@@ -150,7 +146,8 @@ describe('Simple Positioning Engine', () => {
|
|
150
146
|
createTestIdentifier('user', 1, 1),
|
151
147
|
]
|
152
148
|
|
153
|
-
|
149
|
+
const codeElement = container.querySelector('code')!
|
150
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
154
151
|
|
155
152
|
// Should still only have one wrapped element
|
156
153
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -162,11 +159,9 @@ describe('Simple Positioning Engine', () => {
|
|
162
159
|
const container = document.createElement('div')
|
163
160
|
container.innerHTML = `
|
164
161
|
<pre class="shiki">
|
165
|
-
<code>
|
166
|
-
|
167
|
-
|
168
|
-
<span class="line"> user</span>
|
169
|
-
</code>
|
162
|
+
<code>query {
|
163
|
+
|
164
|
+
user</code>
|
170
165
|
</pre>
|
171
166
|
`
|
172
167
|
|
@@ -175,8 +170,9 @@ describe('Simple Positioning Engine', () => {
|
|
175
170
|
createTestIdentifier('user', 3, 3),
|
176
171
|
]
|
177
172
|
|
173
|
+
const codeElement = container.querySelector('code')!
|
178
174
|
expect(() => {
|
179
|
-
calculator.prepareCodeBlock(
|
175
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
180
176
|
}).not.toThrow()
|
181
177
|
|
182
178
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -50,21 +50,104 @@ export class SimplePositionCalculator {
|
|
50
50
|
containerElement: Element,
|
51
51
|
identifiers: Identifier[],
|
52
52
|
): void {
|
53
|
-
//
|
54
|
-
const
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
// Get the full text content of the container
|
54
|
+
const fullText = containerElement.textContent || ''
|
55
|
+
const lines = fullText.split('\n')
|
56
|
+
|
57
|
+
// Build a map of line start positions in the full text
|
58
|
+
const lineStartPositions: number[] = [0]
|
59
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
60
|
+
const lineLength = lines[i]?.length ?? 0
|
61
|
+
lineStartPositions.push(lineStartPositions[i]! + lineLength + 1) // +1 for newline
|
62
|
+
}
|
63
|
+
|
64
|
+
// Process identifiers by line
|
65
|
+
for (const identifier of identifiers) {
|
66
|
+
const lineIndex = identifier.position.line - 1
|
67
|
+
if (lineIndex >= lines.length || lineIndex < 0) continue
|
68
|
+
|
69
|
+
const lineText = lines[lineIndex]
|
70
|
+
if (!lineText) continue
|
71
|
+
|
72
|
+
const columnIndex = identifier.position.column - 1
|
73
|
+
|
74
|
+
// Check if the identifier exists at the expected position
|
75
|
+
if (lineText.substring(columnIndex).startsWith(identifier.name)) {
|
76
|
+
// Calculate the absolute position in the full text
|
77
|
+
const lineStartPosition = lineStartPositions[lineIndex] ?? 0
|
78
|
+
const absolutePosition = lineStartPosition + columnIndex
|
79
|
+
|
80
|
+
// Check if already wrapped at this specific position
|
81
|
+
const existingWrapped = containerElement.querySelectorAll(`[data-graphql-id]`)
|
82
|
+
let alreadyWrapped = false
|
83
|
+
for (const wrapped of existingWrapped) {
|
84
|
+
if (wrapped.textContent === identifier.name) {
|
85
|
+
const startPos = parseInt(wrapped.getAttribute('data-graphql-start') || '0')
|
86
|
+
if (startPos === identifier.position.start) {
|
87
|
+
alreadyWrapped = true
|
88
|
+
break
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
if (alreadyWrapped) continue
|
93
|
+
|
94
|
+
// Create wrapper span
|
95
|
+
const wrapper = document.createElement('span')
|
96
|
+
const id = `${identifier.position.start}-${identifier.name}-${identifier.kind}`
|
97
|
+
wrapper.setAttribute('data-graphql-id', id)
|
98
|
+
wrapper.setAttribute('data-graphql-name', identifier.name)
|
99
|
+
wrapper.setAttribute('data-graphql-kind', identifier.kind)
|
100
|
+
wrapper.setAttribute('data-graphql-start', String(identifier.position.start))
|
101
|
+
wrapper.setAttribute('data-graphql-end', String(identifier.position.end))
|
102
|
+
wrapper.setAttribute('data-graphql-line', String(identifier.position.line))
|
103
|
+
wrapper.setAttribute('data-graphql-column', String(identifier.position.column))
|
104
|
+
wrapper.setAttribute('data-graphql-path', identifier.schemaPath.join(','))
|
105
|
+
|
106
|
+
// Find the position in the container and wrap the text
|
107
|
+
const walker = document.createTreeWalker(
|
108
|
+
containerElement,
|
109
|
+
NodeFilter.SHOW_TEXT,
|
110
|
+
null,
|
111
|
+
)
|
112
|
+
|
113
|
+
let currentPos = 0
|
114
|
+
let node: Node | null
|
115
|
+
|
116
|
+
while (node = walker.nextNode()) {
|
117
|
+
const textNode = node as Text
|
118
|
+
const text = textNode.textContent || ''
|
119
|
+
|
120
|
+
// Check if this text node contains our identifier
|
121
|
+
if (currentPos <= absolutePosition && absolutePosition < currentPos + text.length) {
|
122
|
+
const relativePos = absolutePosition - currentPos
|
123
|
+
|
124
|
+
if (text.substring(relativePos).startsWith(identifier.name)) {
|
125
|
+
// Split the text node
|
126
|
+
const before = text.substring(0, relativePos)
|
127
|
+
const identifierText = identifier.name
|
128
|
+
const after = text.substring(relativePos + identifierText.length)
|
129
|
+
|
130
|
+
const parent = textNode.parentNode!
|
131
|
+
|
132
|
+
if (before) {
|
133
|
+
parent.insertBefore(document.createTextNode(before), textNode)
|
134
|
+
}
|
135
|
+
|
136
|
+
wrapper.textContent = identifierText
|
137
|
+
parent.insertBefore(wrapper, textNode)
|
138
|
+
|
139
|
+
if (after) {
|
140
|
+
parent.insertBefore(document.createTextNode(after), textNode)
|
141
|
+
}
|
142
|
+
|
143
|
+
parent.removeChild(textNode)
|
144
|
+
break
|
145
|
+
}
|
146
|
+
}
|
62
147
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
const wrapped = this.wrapIdentifier(containerElement, identifier)
|
67
|
-
if (wrapped) wrappedCount++
|
148
|
+
currentPos += text.length
|
149
|
+
}
|
150
|
+
}
|
68
151
|
}
|
69
152
|
}
|
70
153
|
|
@@ -117,100 +200,6 @@ export class SimplePositionCalculator {
|
|
117
200
|
|
118
201
|
return results
|
119
202
|
}
|
120
|
-
|
121
|
-
/**
|
122
|
-
* Wrap an identifier in a span for positioning
|
123
|
-
* Returns true if the identifier was successfully wrapped
|
124
|
-
*/
|
125
|
-
private wrapIdentifier(containerElement: Element, identifier: Identifier): boolean {
|
126
|
-
const walker = document.createTreeWalker(
|
127
|
-
containerElement,
|
128
|
-
NodeFilter.SHOW_TEXT,
|
129
|
-
null,
|
130
|
-
)
|
131
|
-
|
132
|
-
let currentLine = 1
|
133
|
-
let currentColumn = 1
|
134
|
-
let node: Node | null
|
135
|
-
|
136
|
-
while (node = walker.nextNode()) {
|
137
|
-
const textNode = node as Text
|
138
|
-
const text = textNode.textContent || ''
|
139
|
-
|
140
|
-
// Check if already wrapped
|
141
|
-
if (textNode.parentElement?.hasAttribute('data-graphql-id')) {
|
142
|
-
// Update position tracking and continue
|
143
|
-
for (const char of text) {
|
144
|
-
if (char === '\n') {
|
145
|
-
currentLine++
|
146
|
-
currentColumn = 1
|
147
|
-
} else {
|
148
|
-
currentColumn++
|
149
|
-
}
|
150
|
-
}
|
151
|
-
continue
|
152
|
-
}
|
153
|
-
|
154
|
-
// Track position in the text
|
155
|
-
let textIndex = 0
|
156
|
-
while (textIndex < text.length) {
|
157
|
-
// Check if we're at the identifier's position
|
158
|
-
if (
|
159
|
-
currentLine === identifier.position.line
|
160
|
-
&& currentColumn === identifier.position.column
|
161
|
-
) {
|
162
|
-
// Verify it's actually our identifier
|
163
|
-
const remainingText = text.substring(textIndex)
|
164
|
-
if (remainingText.startsWith(identifier.name)) {
|
165
|
-
// Create a unique ID for this identifier
|
166
|
-
const id = `${identifier.position.start}-${identifier.name}-${identifier.kind}`
|
167
|
-
|
168
|
-
// Split the text node and wrap the identifier
|
169
|
-
const before = text.substring(0, textIndex)
|
170
|
-
const after = text.substring(textIndex + identifier.name.length)
|
171
|
-
|
172
|
-
const span = document.createElement('span')
|
173
|
-
span.setAttribute('data-graphql-id', id)
|
174
|
-
span.setAttribute('data-graphql-name', identifier.name)
|
175
|
-
span.setAttribute('data-graphql-kind', identifier.kind)
|
176
|
-
span.setAttribute('data-graphql-start', String(identifier.position.start))
|
177
|
-
span.setAttribute('data-graphql-end', String(identifier.position.end))
|
178
|
-
span.setAttribute('data-graphql-line', String(identifier.position.line))
|
179
|
-
span.setAttribute('data-graphql-column', String(identifier.position.column))
|
180
|
-
span.setAttribute('data-graphql-path', identifier.schemaPath.join(','))
|
181
|
-
span.textContent = identifier.name
|
182
|
-
|
183
|
-
const parent = textNode.parentNode!
|
184
|
-
|
185
|
-
if (before) {
|
186
|
-
parent.insertBefore(document.createTextNode(before), textNode)
|
187
|
-
}
|
188
|
-
|
189
|
-
parent.insertBefore(span, textNode)
|
190
|
-
|
191
|
-
if (after) {
|
192
|
-
parent.insertBefore(document.createTextNode(after), textNode)
|
193
|
-
}
|
194
|
-
|
195
|
-
parent.removeChild(textNode)
|
196
|
-
return true
|
197
|
-
}
|
198
|
-
}
|
199
|
-
|
200
|
-
// Update position tracking
|
201
|
-
const char = text[textIndex]
|
202
|
-
if (char === '\n') {
|
203
|
-
currentLine++
|
204
|
-
currentColumn = 1
|
205
|
-
} else {
|
206
|
-
currentColumn++
|
207
|
-
}
|
208
|
-
textIndex++
|
209
|
-
}
|
210
|
-
}
|
211
|
-
|
212
|
-
return false // Identifier not found
|
213
|
-
}
|
214
203
|
}
|
215
204
|
|
216
205
|
/**
|
package/src/lib/kit-temp.test.ts
CHANGED
@@ -74,8 +74,13 @@ describe('property-based tests', () => {
|
|
74
74
|
expect(inAllowed).toBe(!inDenied)
|
75
75
|
})
|
76
76
|
|
77
|
-
// Combined they reconstruct the original object
|
78
|
-
|
77
|
+
// Combined they reconstruct the original object (only own properties)
|
78
|
+
const reconstructed = { ...allowed, ...denied }
|
79
|
+
const ownPropsObj = Object.keys(obj).reduce((acc, key) => {
|
80
|
+
acc[key] = obj[key]
|
81
|
+
return acc
|
82
|
+
}, {} as any)
|
83
|
+
expect(reconstructed).toEqual(ownPropsObj)
|
79
84
|
},
|
80
85
|
),
|
81
86
|
)
|
@@ -87,7 +92,14 @@ describe('property-based tests', () => {
|
|
87
92
|
fc.object(),
|
88
93
|
(obj) => {
|
89
94
|
const filtered = objFilter(obj, () => true)
|
90
|
-
|
95
|
+
|
96
|
+
// Object.keys doesn't include __proto__, so we need to handle it specially
|
97
|
+
const objWithoutProto = Object.keys(obj).reduce((acc, key) => {
|
98
|
+
acc[key] = obj[key]
|
99
|
+
return acc
|
100
|
+
}, {} as any)
|
101
|
+
|
102
|
+
expect(filtered).toEqual(objWithoutProto)
|
91
103
|
|
92
104
|
// Values are the same reference
|
93
105
|
Object.keys(filtered).forEach(key => {
|