htmlgraph 0.24.1__py3-none-any.whl → 0.25.0__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.
Files changed (103) hide show
  1. htmlgraph/__init__.py +20 -1
  2. htmlgraph/agent_detection.py +26 -10
  3. htmlgraph/analytics/cross_session.py +4 -3
  4. htmlgraph/analytics/work_type.py +52 -16
  5. htmlgraph/analytics_index.py +51 -19
  6. htmlgraph/api/__init__.py +3 -0
  7. htmlgraph/api/main.py +2115 -0
  8. htmlgraph/api/static/htmx.min.js +1 -0
  9. htmlgraph/api/static/style-redesign.css +1344 -0
  10. htmlgraph/api/static/style.css +1079 -0
  11. htmlgraph/api/templates/dashboard-redesign.html +812 -0
  12. htmlgraph/api/templates/dashboard.html +783 -0
  13. htmlgraph/api/templates/partials/activity-feed-hierarchical.html +326 -0
  14. htmlgraph/api/templates/partials/activity-feed.html +570 -0
  15. htmlgraph/api/templates/partials/agents-redesign.html +317 -0
  16. htmlgraph/api/templates/partials/agents.html +317 -0
  17. htmlgraph/api/templates/partials/event-traces.html +373 -0
  18. htmlgraph/api/templates/partials/features-kanban-redesign.html +509 -0
  19. htmlgraph/api/templates/partials/features.html +509 -0
  20. htmlgraph/api/templates/partials/metrics-redesign.html +346 -0
  21. htmlgraph/api/templates/partials/metrics.html +346 -0
  22. htmlgraph/api/templates/partials/orchestration-redesign.html +443 -0
  23. htmlgraph/api/templates/partials/orchestration.html +163 -0
  24. htmlgraph/api/templates/partials/spawners.html +375 -0
  25. htmlgraph/atomic_ops.py +560 -0
  26. htmlgraph/builders/base.py +55 -1
  27. htmlgraph/builders/bug.py +17 -2
  28. htmlgraph/builders/chore.py +17 -2
  29. htmlgraph/builders/epic.py +17 -2
  30. htmlgraph/builders/feature.py +25 -2
  31. htmlgraph/builders/phase.py +17 -2
  32. htmlgraph/builders/spike.py +27 -2
  33. htmlgraph/builders/track.py +14 -0
  34. htmlgraph/cigs/__init__.py +4 -0
  35. htmlgraph/cigs/reporter.py +818 -0
  36. htmlgraph/cli.py +1427 -401
  37. htmlgraph/cli_commands/__init__.py +1 -0
  38. htmlgraph/cli_commands/feature.py +195 -0
  39. htmlgraph/cli_framework.py +115 -0
  40. htmlgraph/collections/__init__.py +2 -0
  41. htmlgraph/collections/base.py +21 -0
  42. htmlgraph/collections/session.py +189 -0
  43. htmlgraph/collections/spike.py +7 -1
  44. htmlgraph/collections/task_delegation.py +236 -0
  45. htmlgraph/collections/traces.py +482 -0
  46. htmlgraph/config.py +113 -0
  47. htmlgraph/converter.py +41 -0
  48. htmlgraph/cost_analysis/__init__.py +5 -0
  49. htmlgraph/cost_analysis/analyzer.py +438 -0
  50. htmlgraph/dashboard.html +3315 -492
  51. htmlgraph-0.24.1.data/data/htmlgraph/dashboard.html → htmlgraph/dashboard.html.backup +2246 -248
  52. htmlgraph/dashboard.html.bak +7181 -0
  53. htmlgraph/dashboard.html.bak2 +7231 -0
  54. htmlgraph/dashboard.html.bak3 +7232 -0
  55. htmlgraph/db/__init__.py +38 -0
  56. htmlgraph/db/queries.py +790 -0
  57. htmlgraph/db/schema.py +1334 -0
  58. htmlgraph/deploy.py +26 -27
  59. htmlgraph/docs/API_REFERENCE.md +841 -0
  60. htmlgraph/docs/HTTP_API.md +750 -0
  61. htmlgraph/docs/INTEGRATION_GUIDE.md +752 -0
  62. htmlgraph/docs/ORCHESTRATION_PATTERNS.md +710 -0
  63. htmlgraph/docs/README.md +533 -0
  64. htmlgraph/docs/version_check.py +3 -1
  65. htmlgraph/error_handler.py +544 -0
  66. htmlgraph/event_log.py +2 -0
  67. htmlgraph/hooks/__init__.py +8 -0
  68. htmlgraph/hooks/bootstrap.py +169 -0
  69. htmlgraph/hooks/context.py +271 -0
  70. htmlgraph/hooks/drift_handler.py +521 -0
  71. htmlgraph/hooks/event_tracker.py +405 -15
  72. htmlgraph/hooks/post_tool_use_handler.py +257 -0
  73. htmlgraph/hooks/pretooluse.py +476 -6
  74. htmlgraph/hooks/prompt_analyzer.py +648 -0
  75. htmlgraph/hooks/session_handler.py +583 -0
  76. htmlgraph/hooks/state_manager.py +501 -0
  77. htmlgraph/hooks/subagent_stop.py +309 -0
  78. htmlgraph/hooks/task_enforcer.py +39 -0
  79. htmlgraph/models.py +111 -15
  80. htmlgraph/operations/fastapi_server.py +230 -0
  81. htmlgraph/orchestration/headless_spawner.py +22 -14
  82. htmlgraph/pydantic_models.py +476 -0
  83. htmlgraph/quality_gates.py +350 -0
  84. htmlgraph/repo_hash.py +511 -0
  85. htmlgraph/sdk.py +348 -10
  86. htmlgraph/server.py +194 -0
  87. htmlgraph/session_hooks.py +300 -0
  88. htmlgraph/session_manager.py +131 -1
  89. htmlgraph/session_registry.py +587 -0
  90. htmlgraph/session_state.py +436 -0
  91. htmlgraph/system_prompts.py +449 -0
  92. htmlgraph/templates/orchestration-view.html +350 -0
  93. htmlgraph/track_builder.py +19 -0
  94. htmlgraph/validation.py +115 -0
  95. htmlgraph-0.25.0.data/data/htmlgraph/dashboard.html +7417 -0
  96. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/METADATA +91 -64
  97. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/RECORD +103 -42
  98. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/styles.css +0 -0
  99. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/AGENTS.md.template +0 -0
  100. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/CLAUDE.md.template +0 -0
  101. {htmlgraph-0.24.1.data → htmlgraph-0.25.0.data}/data/htmlgraph/templates/GEMINI.md.template +0 -0
  102. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/WHEEL +0 -0
  103. {htmlgraph-0.24.1.dist-info → htmlgraph-0.25.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,570 @@
1
+ <!-- Grouped Activity Feed - Display events organized by conversation turn (user prompt) -->
2
+ <div class="view-container activity-feed-view">
3
+ <div class="view-header">
4
+ <h2>Agent Activity Feed</h2>
5
+ <div class="view-info">
6
+ <small>Events grouped by conversation turn. Click to expand/collapse child events.</small>
7
+ </div>
8
+ <div class="view-filters">
9
+ <div class="trace-controls">
10
+ <button type="button" class="btn-small" onclick="expandAllConversationTurns()" title="Expand all conversation turns">Expand All</button>
11
+ <button type="button" class="btn-small" onclick="collapseAllConversationTurns()" title="Collapse all conversation turns">Collapse All</button>
12
+ </div>
13
+ </div>
14
+ </div>
15
+
16
+ <!-- Spawner Activity Filter -->
17
+ <div class="spawner-filter">
18
+ <label for="agent-type-filter">Filter:</label>
19
+ <select id="agent-type-filter" onchange="filterByAgentType(this.value)">
20
+ <option value="all">All Activity</option>
21
+ <option value="direct">Direct Actions Only</option>
22
+ <option value="spawner">Spawner Delegations Only</option>
23
+ <optgroup label="Specific Spawners">
24
+ <option value="gemini">Gemini (FREE)</option>
25
+ <option value="codex">Codex (Paid)</option>
26
+ <option value="copilot">Copilot (GitHub)</option>
27
+ </optgroup>
28
+ </select>
29
+ </div>
30
+
31
+ <div class="conversation-feed">
32
+ {% if conversation_turns %}
33
+ <div class="conversation-turns-list">
34
+ {% for turn in conversation_turns %}
35
+ <!-- Conversation Turn Container -->
36
+ <div class="conversation-turn"
37
+ data-turn-id="{{ turn.userQuery.event_id }}"
38
+ data-spawner-type="{% if turn.has_spawner %}spawner{% else %}direct{% endif %}"
39
+ data-agent="{{ turn.userQuery.agent_id }}">
40
+ <!-- User Query Parent Row (Clickable) -->
41
+ <div class="userquery-parent"
42
+ onclick="toggleConversationTurn('{{ turn.userQuery.event_id }}')"
43
+ data-turn-id="{{ turn.userQuery.event_id }}">
44
+
45
+ <!-- Expand/Collapse Toggle -->
46
+ <span class="expand-toggle-turn" id="toggle-{{ turn.userQuery.event_id }}">▶</span>
47
+
48
+ <!-- User Prompt Text -->
49
+ <div class="prompt-section">
50
+ <span class="prompt-text" title="{{ turn.userQuery.prompt }}">
51
+ {{ turn.userQuery.prompt[:100] }}{% if turn.userQuery.prompt|length > 100 %}...{% endif %}
52
+ </span>
53
+ </div>
54
+
55
+ <!-- Stats Badges -->
56
+ <div class="turn-stats">
57
+ {% if turn.stats.tool_count > 0 %}
58
+ <span class="stat-badge tool-count" title="Tool calls">
59
+ {{ turn.stats.tool_count }}
60
+ </span>
61
+ {% endif %}
62
+
63
+ <span class="stat-badge duration" title="Total duration">
64
+ {{ "%.2f"|format(turn.stats.total_duration) }}s
65
+ </span>
66
+
67
+ {% if turn.stats.success_count > 0 %}
68
+ <span class="stat-badge success" title="Successful operations">
69
+ ✓ {{ turn.stats.success_count }}
70
+ </span>
71
+ {% endif %}
72
+
73
+ {% if turn.stats.error_count > 0 %}
74
+ <span class="stat-badge error" title="Errors">
75
+ ✗ {{ turn.stats.error_count }}
76
+ </span>
77
+ {% endif %}
78
+ </div>
79
+
80
+ <!-- Timestamp -->
81
+ <div class="turn-timestamp">
82
+ {{ turn.userQuery.timestamp }}
83
+ </div>
84
+ </div>
85
+
86
+ <!-- Child Events Container (Hidden by default) -->
87
+ <div class="turn-children collapsed" id="children-{{ turn.userQuery.event_id }}">
88
+ {% if turn.children %}
89
+ {% for child in turn.children %}
90
+ <!-- Child Event Row -->
91
+ <div class="child-event-row" data-event-id="{{ child.event_id }}">
92
+ <!-- Tree Connector -->
93
+ <span class="tree-connector">
94
+ {% if loop.last %}└─{% else %}├─{% endif %}
95
+ </span>
96
+
97
+ <!-- Tool Name -->
98
+ <span class="child-tool-name">{{ child.tool_name }}</span>
99
+
100
+ <!-- Summary/Input -->
101
+ <span class="child-summary" title="{{ child.summary }}">
102
+ {{ child.summary[:80] }}{% if child.summary|length > 80 %}...{% endif %}
103
+ </span>
104
+
105
+ <!-- Agent Badge with Spawner Support -->
106
+ {% if child.spawner_type %}
107
+ <!-- Spawner delegation: show orchestrator → spawned AI -->
108
+ <span class="child-agent-badge agent-{{ child.agent|lower|replace(' ', '-') }}">
109
+ {{ child.agent }}
110
+ </span>
111
+ <span class="delegation-arrow">→</span>
112
+ <span class="spawner-badge spawner-{{ child.spawner_type|lower }}">
113
+ {{ child.spawned_agent or child.subagent_type or child.spawner_type }}
114
+ {% if child.cost_usd %}
115
+ <span class="cost-badge">${{ "%.2f"|format(child.cost_usd) }}</span>
116
+ {% endif %}
117
+ </span>
118
+ {% else %}
119
+ <!-- Regular agent: just show agent name -->
120
+ <span class="child-agent-badge agent-{{ child.agent|lower|replace(' ', '-') }}">
121
+ {{ child.agent }}
122
+ </span>
123
+ {% endif %}
124
+
125
+ <!-- Duration -->
126
+ <span class="child-duration">
127
+ {{ "%.2f"|format(child.duration_seconds) }}s
128
+ </span>
129
+
130
+ <!-- Timestamp -->
131
+ <span class="child-timestamp">
132
+ {{ child.timestamp }}
133
+ </span>
134
+ </div>
135
+ {% endfor %}
136
+ {% else %}
137
+ <div class="no-children-message">
138
+ <span class="tree-connector">└─</span>
139
+ <span class="text-muted">No child events</span>
140
+ </div>
141
+ {% endif %}
142
+ </div>
143
+ </div>
144
+ {% endfor %}
145
+ </div>
146
+ {% else %}
147
+ <div class="empty-state">
148
+ <p>No conversation turns found</p>
149
+ <small>Agent activity will appear here as tasks are executed</small>
150
+ </div>
151
+ {% endif %}
152
+ </div>
153
+
154
+ <!-- Auto-refresh indicator -->
155
+ <div class="auto-refresh-indicator">
156
+ <span class="refresh-dot"></span>
157
+ Live updates enabled (via WebSocket)
158
+ </div>
159
+ </div>
160
+
161
+ <style>
162
+ /* ============================================
163
+ Grouped Activity Feed - Conversation Turns
164
+ ============================================ */
165
+
166
+ .conversation-feed {
167
+ display: flex;
168
+ flex-direction: column;
169
+ font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
170
+ font-size: 0.8rem;
171
+ }
172
+
173
+ /* Conversation Turns List Container */
174
+ .conversation-turns-list {
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: 0.5rem;
178
+ padding: 0.5rem;
179
+ }
180
+
181
+ /* Individual Conversation Turn */
182
+ .conversation-turn {
183
+ border-radius: 4px;
184
+ overflow: hidden;
185
+ background: var(--bg-base);
186
+ border: 1px solid var(--border-subtle);
187
+ }
188
+
189
+ /* User Query Parent Row - Clickable Header */
190
+ .userquery-parent {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 0.75rem;
194
+ padding: 1rem;
195
+ background: rgba(200, 255, 0, 0.08);
196
+ border-left: 3px solid var(--accent, #c8ff00);
197
+ cursor: pointer;
198
+ transition: all 0.15s ease;
199
+ user-select: none;
200
+ }
201
+
202
+ .userquery-parent:hover {
203
+ background: rgba(200, 255, 0, 0.12);
204
+ border-left-color: #a3e635;
205
+ }
206
+
207
+ /* Expand/Collapse Toggle */
208
+ .expand-toggle-turn {
209
+ display: inline-flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ width: 24px;
213
+ height: 24px;
214
+ font-size: 0.8rem;
215
+ color: var(--accent-lime, #a3e635);
216
+ transition: transform 0.2s ease;
217
+ flex-shrink: 0;
218
+ }
219
+
220
+ .expand-toggle-turn.expanded {
221
+ transform: rotate(90deg);
222
+ }
223
+
224
+ /* Prompt Section */
225
+ .prompt-section {
226
+ display: flex;
227
+ align-items: center;
228
+ gap: 0.5rem;
229
+ flex: 1;
230
+ min-width: 0;
231
+ }
232
+
233
+ .prompt-icon {
234
+ font-size: 1.1rem;
235
+ flex-shrink: 0;
236
+ }
237
+
238
+ .prompt-text {
239
+ color: var(--text-primary);
240
+ font-weight: 500;
241
+ overflow: hidden;
242
+ text-overflow: ellipsis;
243
+ white-space: nowrap;
244
+ }
245
+
246
+ /* Turn Stats Container */
247
+ .turn-stats {
248
+ display: flex;
249
+ align-items: center;
250
+ gap: 0.4rem;
251
+ flex-shrink: 0;
252
+ }
253
+
254
+ /* Stat Badges */
255
+ .stat-badge {
256
+ display: inline-block;
257
+ padding: 0.25rem 0.5rem;
258
+ font-size: 0.65rem;
259
+ font-weight: 600;
260
+ text-transform: uppercase;
261
+ letter-spacing: 0.05em;
262
+ border-radius: 3px;
263
+ background: rgba(255, 255, 255, 0.1);
264
+ color: var(--text-secondary);
265
+ white-space: nowrap;
266
+ }
267
+
268
+ .stat-badge.tool-count {
269
+ background: rgba(163, 230, 53, 0.2);
270
+ color: #a3e635;
271
+ }
272
+
273
+ .stat-badge.duration {
274
+ background: rgba(100, 200, 255, 0.2);
275
+ color: #64c8ff;
276
+ }
277
+
278
+ .stat-badge.success {
279
+ background: rgba(34, 197, 94, 0.2);
280
+ color: #22c55e;
281
+ }
282
+
283
+ .stat-badge.error {
284
+ background: rgba(239, 68, 68, 0.2);
285
+ color: #ef4444;
286
+ }
287
+
288
+ /* Turn Timestamp */
289
+ .turn-timestamp {
290
+ font-size: 0.65rem;
291
+ color: var(--text-muted);
292
+ flex-shrink: 0;
293
+ min-width: 140px;
294
+ text-align: right;
295
+ }
296
+
297
+ /* Child Events Container */
298
+ .turn-children {
299
+ display: flex;
300
+ flex-direction: column;
301
+ background: rgba(163, 230, 53, 0.02);
302
+ border-top: 1px solid var(--border-subtle);
303
+ padding: 0.5rem 0rem;
304
+ }
305
+
306
+ .turn-children.collapsed {
307
+ display: none;
308
+ }
309
+
310
+ /* Child Event Row */
311
+ .child-event-row {
312
+ display: flex;
313
+ align-items: center;
314
+ gap: 0.2rem;
315
+ padding: 0.5rem 0;
316
+ border-bottom: 1px solid rgba(163, 230, 53, 0.1);
317
+ font-size: 0.75rem;
318
+ margin-left: 0;
319
+ }
320
+
321
+ .child-event-row:last-child {
322
+ border-bottom: none;
323
+ }
324
+
325
+ /* Tree Connector */
326
+ .tree-connector {
327
+ color: var(--accent-lime, #a3e635);
328
+ font-size: 0.9rem;
329
+ font-weight: bold;
330
+ font-family: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
331
+ white-space: pre;
332
+ flex-shrink: 0;
333
+ min-width: 16px;
334
+ }
335
+
336
+ /* Child Tool Name */
337
+ .child-tool-name {
338
+ display: inline-block;
339
+ padding: 0.1rem 0.3rem;
340
+ background: rgba(255, 255, 255, 0.05);
341
+ color: var(--text-primary);
342
+ border-radius: 2px;
343
+ font-weight: 500;
344
+ flex-shrink: 0;
345
+ }
346
+
347
+ /* Child Summary */
348
+ .child-summary {
349
+ color: var(--text-secondary);
350
+ flex: 1;
351
+ min-width: 0;
352
+ overflow: hidden;
353
+ text-overflow: ellipsis;
354
+ white-space: nowrap;
355
+ }
356
+
357
+ /* Child Agent Badge */
358
+ .child-agent-badge {
359
+ display: inline-block;
360
+ padding: 0.1rem 0.35rem;
361
+ font-size: 0.6rem;
362
+ font-weight: 600;
363
+ text-transform: uppercase;
364
+ letter-spacing: 0.05em;
365
+ border-radius: 2px;
366
+ background: rgba(255, 255, 255, 0.08);
367
+ color: var(--text-secondary);
368
+ flex-shrink: 0;
369
+ }
370
+
371
+ .child-agent-badge.agent-claude-code,
372
+ .child-agent-badge.agent-claude {
373
+ background: rgba(200, 255, 0, 0.15);
374
+ color: #c8ff00;
375
+ }
376
+
377
+ .child-agent-badge.agent-gemini,
378
+ .child-agent-badge.agent-gemini-2-0-flash-exp {
379
+ background: rgba(74, 222, 128, 0.15);
380
+ color: #4ade80;
381
+ }
382
+
383
+ /* Child Duration */
384
+ .child-duration {
385
+ display: inline-block;
386
+ padding: 0.1rem 0.3rem;
387
+ background: rgba(100, 200, 255, 0.1);
388
+ color: #64c8ff;
389
+ border-radius: 2px;
390
+ font-size: 0.65rem;
391
+ flex-shrink: 0;
392
+ }
393
+
394
+ /* Child Timestamp */
395
+ .child-timestamp {
396
+ color: var(--text-muted);
397
+ font-size: 0.65rem;
398
+ flex-shrink: 0;
399
+ min-width: 140px;
400
+ text-align: right;
401
+ }
402
+
403
+ /* No Children Message */
404
+ .no-children-message {
405
+ display: flex;
406
+ align-items: center;
407
+ gap: 0.5rem;
408
+ padding: 0.6rem 0;
409
+ color: var(--text-muted);
410
+ font-size: 0.75rem;
411
+ }
412
+
413
+ /* ============================================
414
+ Control Buttons
415
+ ============================================ */
416
+
417
+ .trace-controls {
418
+ display: flex;
419
+ gap: 0.5rem;
420
+ }
421
+
422
+ .btn-small {
423
+ padding: 0.3rem 0.6rem;
424
+ font-size: 0.65rem;
425
+ background: var(--bg-darker);
426
+ border: 1px solid var(--border-subtle);
427
+ color: var(--text-secondary);
428
+ cursor: pointer;
429
+ border-radius: 2px;
430
+ transition: all 0.15s ease;
431
+ text-transform: uppercase;
432
+ letter-spacing: 0.05em;
433
+ font-family: inherit;
434
+ }
435
+
436
+ .btn-small:hover {
437
+ background: rgba(163, 230, 53, 0.1);
438
+ border-color: var(--accent-lime, #a3e635);
439
+ color: var(--accent-lime, #a3e635);
440
+ }
441
+
442
+ /* ============================================
443
+ Empty State
444
+ ============================================ */
445
+
446
+ .empty-state {
447
+ padding: 3rem;
448
+ text-align: center;
449
+ color: var(--text-muted);
450
+ }
451
+
452
+ .empty-state p {
453
+ font-size: 1rem;
454
+ margin-bottom: 0.5rem;
455
+ }
456
+
457
+ /* ============================================
458
+ Auto-refresh Indicator
459
+ ============================================ */
460
+
461
+ .auto-refresh-indicator {
462
+ display: flex;
463
+ align-items: center;
464
+ gap: 0.5rem;
465
+ padding: 0.5rem 1rem;
466
+ font-size: 0.65rem;
467
+ color: var(--text-muted);
468
+ background: var(--bg-darker);
469
+ border-top: 1px solid var(--border-subtle);
470
+ }
471
+
472
+ .refresh-dot {
473
+ width: 6px;
474
+ height: 6px;
475
+ background: var(--accent-lime, #a3e635);
476
+ border-radius: 50%;
477
+ animation: pulse 2s ease-in-out infinite;
478
+ }
479
+
480
+ @keyframes pulse {
481
+ 0%, 100% { opacity: 1; }
482
+ 50% { opacity: 0.4; }
483
+ }
484
+
485
+ /* ============================================
486
+ Responsive Adjustments
487
+ ============================================ */
488
+
489
+ @media (max-width: 1200px) {
490
+ .turn-timestamp,
491
+ .child-timestamp {
492
+ display: none;
493
+ }
494
+ }
495
+
496
+ @media (max-width: 900px) {
497
+ .turn-stats {
498
+ flex-wrap: wrap;
499
+ }
500
+
501
+ .child-summary {
502
+ max-width: 150px;
503
+ }
504
+ }
505
+
506
+ @media (max-width: 700px) {
507
+ .userquery-parent {
508
+ flex-wrap: wrap;
509
+ gap: 0.5rem;
510
+ }
511
+
512
+ .prompt-section {
513
+ width: 100%;
514
+ order: 2;
515
+ }
516
+
517
+ .turn-stats {
518
+ order: 3;
519
+ }
520
+
521
+ .child-event-row {
522
+ flex-wrap: wrap;
523
+ }
524
+
525
+ .child-summary {
526
+ width: 100%;
527
+ }
528
+ }
529
+ </style>
530
+
531
+ <script>
532
+ // JavaScript functions for expand/collapse functionality
533
+ // Called from onclick handlers in the template
534
+
535
+ function toggleConversationTurn(turnId) {
536
+ const childrenContainer = document.getElementById(`children-${turnId}`);
537
+ const toggleButton = document.getElementById(`toggle-${turnId}`);
538
+
539
+ if (childrenContainer) {
540
+ childrenContainer.classList.toggle('collapsed');
541
+ if (toggleButton) {
542
+ toggleButton.classList.toggle('expanded');
543
+ }
544
+ }
545
+ }
546
+
547
+ function expandAllConversationTurns() {
548
+ // Get all child containers and remove the 'collapsed' class
549
+ document.querySelectorAll('.turn-children').forEach(container => {
550
+ container.classList.remove('collapsed');
551
+ });
552
+
553
+ // Rotate all toggle buttons
554
+ document.querySelectorAll('.expand-toggle-turn').forEach(toggle => {
555
+ toggle.classList.add('expanded');
556
+ });
557
+ }
558
+
559
+ function collapseAllConversationTurns() {
560
+ // Get all child containers and add the 'collapsed' class
561
+ document.querySelectorAll('.turn-children').forEach(container => {
562
+ container.classList.add('collapsed');
563
+ });
564
+
565
+ // Rotate all toggle buttons back
566
+ document.querySelectorAll('.expand-toggle-turn').forEach(toggle => {
567
+ toggle.classList.remove('expanded');
568
+ });
569
+ }
570
+ </script>