pywire 0.1.0__py3-none-any.whl → 0.1.1__py3-none-any.whl
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.
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/METADATA +23 -1
- pywire-0.1.1.dist-info/RECORD +9 -0
- pywire/__init__.py +0 -2
- pywire/cli/__init__.py +0 -1
- pywire/cli/generators.py +0 -48
- pywire/cli/main.py +0 -309
- pywire/cli/tui.py +0 -563
- pywire/cli/validate.py +0 -26
- pywire/client/.prettierignore +0 -8
- pywire/client/.prettierrc +0 -7
- pywire/client/build.mjs +0 -73
- pywire/client/eslint.config.js +0 -46
- pywire/client/package.json +0 -39
- pywire/client/pnpm-lock.yaml +0 -2971
- pywire/client/src/core/app.ts +0 -263
- pywire/client/src/core/dom-updater.test.ts +0 -78
- pywire/client/src/core/dom-updater.ts +0 -321
- pywire/client/src/core/index.ts +0 -5
- pywire/client/src/core/transport-manager.test.ts +0 -179
- pywire/client/src/core/transport-manager.ts +0 -159
- pywire/client/src/core/transports/base.ts +0 -122
- pywire/client/src/core/transports/http.ts +0 -142
- pywire/client/src/core/transports/index.ts +0 -13
- pywire/client/src/core/transports/websocket.ts +0 -97
- pywire/client/src/core/transports/webtransport.ts +0 -149
- pywire/client/src/dev/dev-app.ts +0 -93
- pywire/client/src/dev/error-trace.test.ts +0 -97
- pywire/client/src/dev/error-trace.ts +0 -76
- pywire/client/src/dev/index.ts +0 -4
- pywire/client/src/dev/status-overlay.ts +0 -63
- pywire/client/src/events/handler.test.ts +0 -318
- pywire/client/src/events/handler.ts +0 -454
- pywire/client/src/pywire.core.ts +0 -22
- pywire/client/src/pywire.dev.ts +0 -27
- pywire/client/tsconfig.json +0 -17
- pywire/client/vitest.config.ts +0 -15
- pywire/compiler/__init__.py +0 -6
- pywire/compiler/ast_nodes.py +0 -304
- pywire/compiler/attributes/__init__.py +0 -6
- pywire/compiler/attributes/base.py +0 -24
- pywire/compiler/attributes/conditional.py +0 -37
- pywire/compiler/attributes/events.py +0 -55
- pywire/compiler/attributes/form.py +0 -37
- pywire/compiler/attributes/loop.py +0 -75
- pywire/compiler/attributes/reactive.py +0 -34
- pywire/compiler/build.py +0 -28
- pywire/compiler/build_artifacts.py +0 -342
- pywire/compiler/codegen/__init__.py +0 -5
- pywire/compiler/codegen/attributes/__init__.py +0 -6
- pywire/compiler/codegen/attributes/base.py +0 -19
- pywire/compiler/codegen/attributes/events.py +0 -35
- pywire/compiler/codegen/directives/__init__.py +0 -6
- pywire/compiler/codegen/directives/base.py +0 -16
- pywire/compiler/codegen/directives/path.py +0 -53
- pywire/compiler/codegen/generator.py +0 -2341
- pywire/compiler/codegen/template.py +0 -2178
- pywire/compiler/directives/__init__.py +0 -7
- pywire/compiler/directives/base.py +0 -20
- pywire/compiler/directives/component.py +0 -33
- pywire/compiler/directives/context.py +0 -93
- pywire/compiler/directives/layout.py +0 -49
- pywire/compiler/directives/no_spa.py +0 -24
- pywire/compiler/directives/path.py +0 -71
- pywire/compiler/directives/props.py +0 -88
- pywire/compiler/exceptions.py +0 -19
- pywire/compiler/interpolation/__init__.py +0 -6
- pywire/compiler/interpolation/base.py +0 -28
- pywire/compiler/interpolation/jinja.py +0 -272
- pywire/compiler/parser.py +0 -750
- pywire/compiler/paths.py +0 -29
- pywire/compiler/preprocessor.py +0 -43
- pywire/core/wire.py +0 -119
- pywire/py.typed +0 -0
- pywire/runtime/__init__.py +0 -7
- pywire/runtime/aioquic_server.py +0 -194
- pywire/runtime/app.py +0 -889
- pywire/runtime/compile_error_page.py +0 -195
- pywire/runtime/debug.py +0 -203
- pywire/runtime/dev_server.py +0 -434
- pywire/runtime/dev_server.py.broken +0 -268
- pywire/runtime/error_page.py +0 -64
- pywire/runtime/error_renderer.py +0 -23
- pywire/runtime/escape.py +0 -23
- pywire/runtime/files.py +0 -40
- pywire/runtime/helpers.py +0 -97
- pywire/runtime/http_transport.py +0 -253
- pywire/runtime/loader.py +0 -272
- pywire/runtime/logging.py +0 -72
- pywire/runtime/page.py +0 -384
- pywire/runtime/pydantic_integration.py +0 -52
- pywire/runtime/router.py +0 -229
- pywire/runtime/server.py +0 -25
- pywire/runtime/style_collector.py +0 -31
- pywire/runtime/upload_manager.py +0 -76
- pywire/runtime/validation.py +0 -449
- pywire/runtime/websocket.py +0 -665
- pywire/runtime/webtransport_handler.py +0 -195
- pywire-0.1.0.dist-info/RECORD +0 -104
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/WHEEL +0 -0
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/entry_points.txt +0 -0
- {pywire-0.1.0.dist-info → pywire-0.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
import { PyWireApp } from '../core/app'
|
|
2
|
-
import { DOMUpdater } from '../core/dom-updater'
|
|
3
|
-
import { EventData } from '../core/transports'
|
|
4
|
-
|
|
5
|
-
// Type alias for backward compatibility
|
|
6
|
-
type Application = PyWireApp
|
|
7
|
-
|
|
8
|
-
export class UnifiedEventHandler {
|
|
9
|
-
private app: Application
|
|
10
|
-
private debouncers = new Map<string, number>()
|
|
11
|
-
private throttlers = new Map<string, number>()
|
|
12
|
-
|
|
13
|
-
private supportedEvents = [
|
|
14
|
-
'click',
|
|
15
|
-
'submit',
|
|
16
|
-
'input',
|
|
17
|
-
'change',
|
|
18
|
-
'keydown',
|
|
19
|
-
'keyup',
|
|
20
|
-
'focus',
|
|
21
|
-
'blur',
|
|
22
|
-
'mouseenter',
|
|
23
|
-
'mouseleave',
|
|
24
|
-
'scroll',
|
|
25
|
-
'contextmenu',
|
|
26
|
-
]
|
|
27
|
-
|
|
28
|
-
// Events that should be suppressed during DOM updates to prevent loops
|
|
29
|
-
private suppressDuringUpdate = ['focus', 'blur', 'mouseenter', 'mouseleave']
|
|
30
|
-
|
|
31
|
-
constructor(app: Application) {
|
|
32
|
-
this.app = app
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
private debugLog(...args: unknown[]): void {
|
|
36
|
-
if (this.app.getConfig().debug) {
|
|
37
|
-
console.log(...args)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Initialize global event listeners.
|
|
43
|
-
* Uses event delegation on document body.
|
|
44
|
-
*/
|
|
45
|
-
init(): void {
|
|
46
|
-
this.supportedEvents.forEach((eventType) => {
|
|
47
|
-
const options =
|
|
48
|
-
eventType === 'mouseenter' ||
|
|
49
|
-
eventType === 'mouseleave' ||
|
|
50
|
-
eventType === 'focus' ||
|
|
51
|
-
eventType === 'blur' ||
|
|
52
|
-
eventType === 'scroll'
|
|
53
|
-
? { capture: true } // These don't bubble nicely or at all in some cases
|
|
54
|
-
: undefined
|
|
55
|
-
|
|
56
|
-
document.addEventListener(eventType, (e) => this.handleEvent(e), options)
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Helper to parse handlers from element (legacy or multiple/JSON).
|
|
62
|
-
*/
|
|
63
|
-
private getHandlers(
|
|
64
|
-
element: HTMLElement,
|
|
65
|
-
eventType: string
|
|
66
|
-
): Array<{ name: string; modifiers: string[]; args?: unknown[] }> {
|
|
67
|
-
const handlerAttr = `data-on-${eventType}`
|
|
68
|
-
const attrValue = element.getAttribute(handlerAttr)
|
|
69
|
-
if (!attrValue) return []
|
|
70
|
-
|
|
71
|
-
if (attrValue.trim().startsWith('[')) {
|
|
72
|
-
try {
|
|
73
|
-
const handlers = JSON.parse(attrValue) as unknown
|
|
74
|
-
if (Array.isArray(handlers)) {
|
|
75
|
-
return handlers.flatMap((handler) => {
|
|
76
|
-
if (!handler || typeof handler !== 'object') return []
|
|
77
|
-
const name =
|
|
78
|
-
'handler' in handler && typeof handler.handler === 'string' ? handler.handler : null
|
|
79
|
-
if (!name) return []
|
|
80
|
-
|
|
81
|
-
const modifiers =
|
|
82
|
-
'modifiers' in handler && Array.isArray(handler.modifiers)
|
|
83
|
-
? handler.modifiers.filter((m: unknown): m is string => typeof m === 'string')
|
|
84
|
-
: []
|
|
85
|
-
|
|
86
|
-
const args = 'args' in handler && Array.isArray(handler.args) ? handler.args : undefined
|
|
87
|
-
|
|
88
|
-
return [{ name, modifiers, args }]
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
} catch (e) {
|
|
92
|
-
console.error('Error parsing event handlers:', e)
|
|
93
|
-
}
|
|
94
|
-
} else {
|
|
95
|
-
// Legacy single handler
|
|
96
|
-
const modifiersAttr = element.getAttribute(`data-modifiers-${eventType}`)
|
|
97
|
-
const modifiers = modifiersAttr ? modifiersAttr.split(' ').filter((m) => m) : []
|
|
98
|
-
return [{ name: attrValue, modifiers, args: undefined }]
|
|
99
|
-
}
|
|
100
|
-
return []
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Main event handler.
|
|
105
|
-
*/
|
|
106
|
-
private async handleEvent(e: Event): Promise<void> {
|
|
107
|
-
const eventType = e.type
|
|
108
|
-
|
|
109
|
-
// Skip focus/blur/mouseenter/mouseleave events during DOM updates to prevent loops
|
|
110
|
-
if (DOMUpdater.isUpdating && this.suppressDuringUpdate.includes(eventType)) {
|
|
111
|
-
this.debugLog(
|
|
112
|
-
'[Handler] SUPPRESSING event during update:',
|
|
113
|
-
eventType,
|
|
114
|
-
'isUpdating=',
|
|
115
|
-
DOMUpdater.isUpdating
|
|
116
|
-
)
|
|
117
|
-
return
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
this.debugLog('[Handler] Processing event:', eventType, 'isUpdating=', DOMUpdater.isUpdating)
|
|
121
|
-
|
|
122
|
-
// 1. Delegated handlers (standard path walk with bubbling)
|
|
123
|
-
const path = e.composedPath ? e.composedPath() : []
|
|
124
|
-
let propagationStopped = false
|
|
125
|
-
|
|
126
|
-
for (const node of path) {
|
|
127
|
-
if (propagationStopped) break
|
|
128
|
-
|
|
129
|
-
if (node instanceof HTMLElement) {
|
|
130
|
-
const element = node
|
|
131
|
-
const handlers = this.getHandlers(element, eventType)
|
|
132
|
-
|
|
133
|
-
if (handlers.length > 0) {
|
|
134
|
-
this.debugLog('[handleEvent] Found handlers on', element.tagName, handlers)
|
|
135
|
-
|
|
136
|
-
for (const h of handlers) {
|
|
137
|
-
// Skip if it's a .window or .outside handler - those are handled globally
|
|
138
|
-
if (!h.modifiers.includes('window') && !h.modifiers.includes('outside')) {
|
|
139
|
-
this.processEvent(element, eventType, h.name, h.modifiers, e, h.args)
|
|
140
|
-
if (e.cancelBubble) propagationStopped = true
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 2. Global handlers (.window, .outside)
|
|
148
|
-
this.handleGlobalEvent(e)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Handle modifiers that listen outside the normal delegation path.
|
|
153
|
-
*/
|
|
154
|
-
private handleGlobalEvent(e: Event): void {
|
|
155
|
-
const eventType = e.type
|
|
156
|
-
const windowSelector = `[data-modifiers-${eventType}*="window"]`
|
|
157
|
-
const outsideSelector = `[data-modifiers-${eventType}*="outside"]`
|
|
158
|
-
|
|
159
|
-
const candidates = document.querySelectorAll(`${windowSelector}, ${outsideSelector}`)
|
|
160
|
-
|
|
161
|
-
candidates.forEach((el) => {
|
|
162
|
-
if (!(el instanceof HTMLElement)) return
|
|
163
|
-
|
|
164
|
-
const handlers = this.getHandlers(el, eventType)
|
|
165
|
-
for (const h of handlers) {
|
|
166
|
-
// .window: trigger regardless of where the event happened
|
|
167
|
-
if (h.modifiers.includes('window')) {
|
|
168
|
-
this.processEvent(el, eventType, h.name, h.modifiers, e, h.args)
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// .outside: trigger if target is NOT inside this element
|
|
172
|
-
if (h.modifiers.includes('outside')) {
|
|
173
|
-
const target = e.target as Node | null
|
|
174
|
-
if (target && !el.contains(target)) {
|
|
175
|
-
this.processEvent(el, eventType, h.name, h.modifiers, e, h.args)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Process an event for a specific element after it has been matched.
|
|
184
|
-
*/
|
|
185
|
-
private processEvent(
|
|
186
|
-
element: HTMLElement,
|
|
187
|
-
eventType: string,
|
|
188
|
-
handlerName: string,
|
|
189
|
-
modifiers: string[],
|
|
190
|
-
e: Event,
|
|
191
|
-
explicitArgs?: unknown[]
|
|
192
|
-
): void {
|
|
193
|
-
this.debugLog('[processEvent]', eventType, 'handler:', handlerName, 'modifiers:', modifiers)
|
|
194
|
-
|
|
195
|
-
// --- 1. Logic Modifers ---
|
|
196
|
-
|
|
197
|
-
// .prevent
|
|
198
|
-
if (modifiers.includes('prevent') || eventType === 'submit') {
|
|
199
|
-
this.debugLog('[processEvent] Calling preventDefault')
|
|
200
|
-
e.preventDefault()
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// .stop
|
|
204
|
-
if (modifiers.includes('stop')) {
|
|
205
|
-
e.stopPropagation()
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// .self
|
|
209
|
-
if (modifiers.includes('self')) {
|
|
210
|
-
if (e.target !== element) return
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// --- 2. Filter Modifiers ---
|
|
214
|
-
|
|
215
|
-
// System modifiers (Shift, Ctrl, Alt, Meta) - supported on Keyboard and Mouse events
|
|
216
|
-
if (modifiers.includes('shift') && (!('shiftKey' in e) || !e.shiftKey)) return
|
|
217
|
-
if (modifiers.includes('ctrl') && (!('ctrlKey' in e) || !e.ctrlKey)) return
|
|
218
|
-
if (modifiers.includes('alt') && (!('altKey' in e) || !e.altKey)) return
|
|
219
|
-
if (modifiers.includes('meta') && (!('metaKey' in e) || !e.metaKey)) return
|
|
220
|
-
if (modifiers.includes('cmd') && (!('metaKey' in e) || !e.metaKey)) return
|
|
221
|
-
|
|
222
|
-
if (e instanceof KeyboardEvent) {
|
|
223
|
-
// Known key modifiers
|
|
224
|
-
const knownKeys = ['enter', 'escape', 'space', 'tab', 'up', 'down', 'left', 'right']
|
|
225
|
-
// System modifiers that should NOT be treated as key constraints
|
|
226
|
-
const systemMods = [
|
|
227
|
-
'shift',
|
|
228
|
-
'ctrl',
|
|
229
|
-
'alt',
|
|
230
|
-
'meta',
|
|
231
|
-
'cmd',
|
|
232
|
-
'window',
|
|
233
|
-
'outside',
|
|
234
|
-
'prevent',
|
|
235
|
-
'stop',
|
|
236
|
-
'self',
|
|
237
|
-
'debounce',
|
|
238
|
-
'throttle',
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
// Key modifiers are anything that's not a system mod and is either a known key or a single character
|
|
242
|
-
const keyModifiers = modifiers.filter((m) => {
|
|
243
|
-
if (systemMods.includes(m)) return false
|
|
244
|
-
if (m.startsWith('debounce') || m.startsWith('throttle')) return false
|
|
245
|
-
if (m.endsWith('ms')) return false // Duration like 500ms
|
|
246
|
-
return knownKeys.includes(m) || m.length === 1
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
if (keyModifiers.length > 0) {
|
|
250
|
-
const pressedKey = e.key.toLowerCase()
|
|
251
|
-
this.debugLog('[processEvent] Key check. Pressed:', pressedKey, 'Modifiers:', keyModifiers)
|
|
252
|
-
|
|
253
|
-
// Map for special keys
|
|
254
|
-
const keyMap: Record<string, string> = {
|
|
255
|
-
escape: 'escape',
|
|
256
|
-
esc: 'escape',
|
|
257
|
-
enter: 'enter',
|
|
258
|
-
space: ' ',
|
|
259
|
-
spacebar: ' ',
|
|
260
|
-
' ': ' ',
|
|
261
|
-
tab: 'tab',
|
|
262
|
-
up: 'arrowup',
|
|
263
|
-
arrowup: 'arrowup',
|
|
264
|
-
down: 'arrowdown',
|
|
265
|
-
arrowdown: 'arrowdown',
|
|
266
|
-
left: 'arrowleft',
|
|
267
|
-
arrowleft: 'arrowleft',
|
|
268
|
-
right: 'arrowright',
|
|
269
|
-
arrowright: 'arrowright',
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Normalize the pressed key
|
|
273
|
-
const normalizedPressedKey = keyMap[pressedKey] || pressedKey
|
|
274
|
-
|
|
275
|
-
// Check if any key constraint matches
|
|
276
|
-
let match = false
|
|
277
|
-
for (const constraint of keyModifiers) {
|
|
278
|
-
const targetKey = keyMap[constraint] || constraint
|
|
279
|
-
this.debugLog(
|
|
280
|
-
'[processEvent] Comparing constraint:',
|
|
281
|
-
constraint,
|
|
282
|
-
'->',
|
|
283
|
-
targetKey,
|
|
284
|
-
'vs',
|
|
285
|
-
normalizedPressedKey,
|
|
286
|
-
'code:',
|
|
287
|
-
e.code
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
// Match against key (normalized)
|
|
291
|
-
if (targetKey === normalizedPressedKey) {
|
|
292
|
-
match = true
|
|
293
|
-
break
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Fallback: match against code (e.g. 'h' matches 'KeyH')
|
|
297
|
-
// This handles cases where modifiers change the key value (e.g. Alt+H -> ˙)
|
|
298
|
-
if (e.code && e.code.toLowerCase() === `key${targetKey}`) {
|
|
299
|
-
match = true
|
|
300
|
-
break
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (!match) {
|
|
304
|
-
this.debugLog('[processEvent] No key match found.')
|
|
305
|
-
return
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// --- 3. Performance Modifiers ---
|
|
311
|
-
const debounceMod = modifiers.find((m) => m.startsWith('debounce'))
|
|
312
|
-
const throttleMod = modifiers.find((m) => m.startsWith('throttle'))
|
|
313
|
-
|
|
314
|
-
const elementId = element.id || this.getUniqueId(element)
|
|
315
|
-
const eventKey = `${elementId}-${eventType}-${handlerName}`
|
|
316
|
-
|
|
317
|
-
if (debounceMod) {
|
|
318
|
-
const duration = this.parseDuration(modifiers, 250)
|
|
319
|
-
|
|
320
|
-
if (this.debouncers.has(eventKey)) {
|
|
321
|
-
window.clearTimeout(this.debouncers.get(eventKey))
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
const timer = window.setTimeout(() => {
|
|
325
|
-
this.debouncers.delete(eventKey)
|
|
326
|
-
this.dispatchEvent(element, eventType, handlerName, e, explicitArgs)
|
|
327
|
-
}, duration)
|
|
328
|
-
|
|
329
|
-
this.debouncers.set(eventKey, timer)
|
|
330
|
-
return
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (throttleMod) {
|
|
334
|
-
const duration = this.parseDuration(modifiers, 250)
|
|
335
|
-
if (this.throttlers.has(eventKey)) return
|
|
336
|
-
|
|
337
|
-
this.throttlers.set(eventKey, Date.now())
|
|
338
|
-
// Execute immediately
|
|
339
|
-
this.dispatchEvent(element, eventType, handlerName, e, explicitArgs)
|
|
340
|
-
|
|
341
|
-
window.setTimeout(() => {
|
|
342
|
-
this.throttlers.delete(eventKey)
|
|
343
|
-
}, duration)
|
|
344
|
-
return
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Direct dispatch
|
|
348
|
-
this.dispatchEvent(element, eventType, handlerName, e, explicitArgs)
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Extract data and send event.
|
|
353
|
-
*/
|
|
354
|
-
private dispatchEvent(
|
|
355
|
-
element: HTMLElement,
|
|
356
|
-
eventType: string,
|
|
357
|
-
handler: string,
|
|
358
|
-
e: Event,
|
|
359
|
-
explicitArgs?: unknown[]
|
|
360
|
-
): void {
|
|
361
|
-
// Merge explicit args (from JSON) into args payload
|
|
362
|
-
let args: Record<string, unknown> = {}
|
|
363
|
-
if (explicitArgs && explicitArgs.length > 0) {
|
|
364
|
-
explicitArgs.forEach((val, i) => {
|
|
365
|
-
args[`arg${i}`] = val
|
|
366
|
-
})
|
|
367
|
-
} else {
|
|
368
|
-
args = this.getArgs(element)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const eventData: EventData = {
|
|
372
|
-
type: eventType,
|
|
373
|
-
id: element.id,
|
|
374
|
-
args: args,
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Extract specific data based on element type
|
|
378
|
-
if (element instanceof HTMLInputElement) {
|
|
379
|
-
eventData.value = element.value
|
|
380
|
-
if (element.type === 'checkbox' || element.type === 'radio') {
|
|
381
|
-
eventData.checked = element.checked
|
|
382
|
-
}
|
|
383
|
-
} else if (element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
384
|
-
eventData.value = element.value
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// Extract Key data
|
|
388
|
-
if (e instanceof KeyboardEvent) {
|
|
389
|
-
eventData.key = e.key
|
|
390
|
-
eventData.keyCode = e.keyCode
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Extract Form Data for submit
|
|
394
|
-
if (eventType === 'submit' && element instanceof HTMLFormElement) {
|
|
395
|
-
const formData = new FormData(element)
|
|
396
|
-
const data: Record<string, string> = {}
|
|
397
|
-
formData.forEach((value, key) => {
|
|
398
|
-
if (!(value instanceof File)) {
|
|
399
|
-
data[key] = value.toString()
|
|
400
|
-
}
|
|
401
|
-
})
|
|
402
|
-
eventData.formData = data
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
this.app.sendEvent(handler, eventData)
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
private parseDuration(modifiers: string[], defaultDuration: number): number {
|
|
409
|
-
const debounceIdx = modifiers.findIndex((m) => m.startsWith('debounce'))
|
|
410
|
-
const throttleIdx = modifiers.findIndex((m) => m.startsWith('throttle'))
|
|
411
|
-
const idx = debounceIdx !== -1 ? debounceIdx : throttleIdx
|
|
412
|
-
|
|
413
|
-
if (idx !== -1 && modifiers[idx + 1]) {
|
|
414
|
-
const next = modifiers[idx + 1]
|
|
415
|
-
if (next.endsWith('ms')) {
|
|
416
|
-
const val = parseInt(next)
|
|
417
|
-
if (!isNaN(val)) return val
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Support hyphenated: debounce-500ms
|
|
422
|
-
const mod = modifiers[idx]
|
|
423
|
-
if (mod && mod.includes('-')) {
|
|
424
|
-
const parts = mod.split('-')
|
|
425
|
-
const val = parseInt(parts[1])
|
|
426
|
-
if (!isNaN(val)) return val
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
return defaultDuration
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
private getUniqueId(element: HTMLElement): string {
|
|
433
|
-
if (!element.id) {
|
|
434
|
-
element.id = 'pywire-uid-' + Math.random().toString(36).substr(2, 9)
|
|
435
|
-
}
|
|
436
|
-
return element.id
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
private getArgs(element: Element): Record<string, unknown> {
|
|
440
|
-
const args: Record<string, unknown> = {}
|
|
441
|
-
if (element instanceof HTMLElement) {
|
|
442
|
-
for (const key in element.dataset) {
|
|
443
|
-
if (key.startsWith('arg')) {
|
|
444
|
-
try {
|
|
445
|
-
args[key] = JSON.parse(element.dataset[key] || 'null')
|
|
446
|
-
} catch {
|
|
447
|
-
args[key] = element.dataset[key]
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
return args
|
|
453
|
-
}
|
|
454
|
-
}
|
pywire/client/src/pywire.core.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PyWire Core Bundle Entry Point
|
|
3
|
-
* Production-optimized client with minimal footprint.
|
|
4
|
-
* Excludes dev features: status overlay, error trace with source loading.
|
|
5
|
-
*/
|
|
6
|
-
import { PyWireApp } from './core/app'
|
|
7
|
-
|
|
8
|
-
export { PyWireApp, PyWireConfig } from './core/app'
|
|
9
|
-
export { TransportManager, TransportConfig } from './core/transport-manager'
|
|
10
|
-
export { DOMUpdater } from './core/dom-updater'
|
|
11
|
-
export * from './core/transports'
|
|
12
|
-
|
|
13
|
-
// Auto-init
|
|
14
|
-
const app = new PyWireApp()
|
|
15
|
-
|
|
16
|
-
if (document.readyState === 'loading') {
|
|
17
|
-
document.addEventListener('DOMContentLoaded', () => app.init())
|
|
18
|
-
} else {
|
|
19
|
-
app.init()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { app }
|
pywire/client/src/pywire.dev.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PyWire Dev Bundle Entry Point
|
|
3
|
-
* Includes all core features plus development tools:
|
|
4
|
-
* - Connection status overlay
|
|
5
|
-
* - Error trace with source loading for DevTools
|
|
6
|
-
* - Enhanced console output
|
|
7
|
-
*/
|
|
8
|
-
import { PyWireDevApp } from './dev/dev-app'
|
|
9
|
-
|
|
10
|
-
// Re-export core
|
|
11
|
-
export * from './core'
|
|
12
|
-
|
|
13
|
-
// Export dev-specific
|
|
14
|
-
export { PyWireDevApp } from './dev/dev-app'
|
|
15
|
-
export { StatusOverlay } from './dev/status-overlay'
|
|
16
|
-
export { ErrorTraceHandler } from './dev/error-trace'
|
|
17
|
-
|
|
18
|
-
// Auto-init with dev app
|
|
19
|
-
const app = new PyWireDevApp()
|
|
20
|
-
|
|
21
|
-
if (document.readyState === 'loading') {
|
|
22
|
-
document.addEventListener('DOMContentLoaded', () => app.init())
|
|
23
|
-
} else {
|
|
24
|
-
app.init()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export { app }
|
pywire/client/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"strict": true,
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
"declaration": false,
|
|
11
|
-
"outDir": "./dist",
|
|
12
|
-
"rootDir": "./src",
|
|
13
|
-
"lib": ["ES2020", "DOM", "DOM.Iterable"]
|
|
14
|
-
},
|
|
15
|
-
"include": ["src/**/*"],
|
|
16
|
-
"exclude": ["node_modules", "dist"]
|
|
17
|
-
}
|
pywire/client/vitest.config.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config'
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
environment: 'happy-dom',
|
|
6
|
-
globals: true,
|
|
7
|
-
include: ['src/**/*.test.ts'],
|
|
8
|
-
coverage: {
|
|
9
|
-
provider: 'v8',
|
|
10
|
-
reporter: ['text', 'json', 'html'],
|
|
11
|
-
include: ['src/**/*.ts'],
|
|
12
|
-
exclude: ['src/**/*.test.ts', 'src/index.ts', 'src/pywire.core.ts', 'src/pywire.dev.ts'],
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
})
|