@bhooai/nexus-graphql 2.0.11 → 2.0.12

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.
@@ -0,0 +1,1495 @@
1
+ /**
2
+ * GraphQL Explorer HTML — served from the backend at GET /graphql (Accept: text/html),
3
+ * GET /graphiql and GET /explorer.
4
+ *
5
+ * Zero build dependency: loads GraphiQL 3 + graphql 16 via CDN, wraps in a
6
+ * BhooAI Nexus branded chrome (glass-card, mesh, aurora tokens) so the
7
+ * playground feels native to the framework. Includes a tiny AI assistant panel
8
+ * that POSTs to /ai/chat (if the AI server is configured) with the schema SDL
9
+ * as context to generate queries from natural language.
10
+ *
11
+ * No auth guard — the explorer is public when introspection is enabled, just
12
+ * like POST /graphql. Disable with `graphql.explorer: false` in nexus.config.ts.
13
+ */
14
+ const AURORA_CSS = `.agent-shell { display:flex; flex-direction:column; height:100vh; height:100dvh; overflow:hidden; background:#040b18; isolation:isolate; }
15
+ .agent-shell::before { position:fixed; inset:0; z-index:0; pointer-events:none; content:""; opacity:.5;
16
+ background-image:linear-gradient(rgba(95,164,241,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(95,164,241,.05) 1px, transparent 1px);
17
+ background-size:48px 48px; mask-image:linear-gradient(to bottom, black, transparent 78%); }
18
+ .agent-aurora { position:fixed; inset:0; z-index:0; overflow:hidden; pointer-events:none; }
19
+ .agent-aurora i { position:absolute; border-radius:50%; filter:blur(90px); opacity:.55; mix-blend-mode:screen; will-change:transform; }
20
+ .agent-aurora i:nth-child(1) { width:44rem; height:44rem; top:-12rem; left:-8rem; background:radial-gradient(circle at 35% 35%, rgba(84,183,255,.5), transparent 68%); animation:agent-drift-a 30s ease-in-out infinite alternate; }
21
+ .agent-aurora i:nth-child(2) { width:40rem; height:40rem; right:-10rem; bottom:-8rem; background:radial-gradient(circle at 60% 60%, rgba(159,123,255,.45), transparent 68%); animation:agent-drift-b 36s ease-in-out infinite alternate; }
22
+ .agent-aurora i:nth-child(3) { width:34rem; height:34rem; top:24%; left:46%; background:radial-gradient(circle at 50% 50%, rgba(243,110,184,.3), transparent 70%); animation:agent-drift-c 26s ease-in-out infinite alternate; }
23
+ @keyframes agent-drift-a { from { transform:translate3d(0,0,0) scale(1); } to { transform:translate3d(10rem,6rem,0) scale(1.18); } }
24
+ @keyframes agent-drift-b { from { transform:translate3d(0,0,0) scale(1.05); } to { transform:translate3d(-9rem,-5rem,0) scale(.9); } }
25
+ @keyframes agent-drift-c { from { transform:translate3d(0,0,0) scale(.95); } to { transform:translate3d(-7rem,9rem,0) scale(1.15); } }
26
+ .agent-shell > .agent-header,
27
+ .agent-shell > .agent-workspace,
28
+ .agent-shell > .agent-footer { position:relative; z-index:1; }
29
+
30
+ .agent-header { position:relative; z-index:20; flex:0 0 auto; background:linear-gradient(180deg, rgba(9,24,48,.9), rgba(4,14,29,.74)); border-bottom:1px solid rgba(112,168,228,.2); box-shadow:inset 0 1px 0 rgba(255,255,255,.05), 0 12px 40px rgba(2,8,20,.45); backdrop-filter:blur(26px); -webkit-backdrop-filter:blur(26px); }
31
+ .agent-header::after { position:absolute; left:24px; right:24px; bottom:-1px; height:1px; content:""; background:linear-gradient(90deg, transparent, rgba(84,183,255,.55), rgba(159,123,255,.5), transparent); pointer-events:none; }
32
+ .agent-header-inner { display:flex; align-items:center; gap:.7rem; row-gap:.5rem; flex-wrap:wrap; width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.72rem 0; }
33
+ .agent-urlbar { display:flex; align-items:center; gap:.4rem; width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.32rem 0 .5rem; }
34
+ .agent-urlbar-input { flex:1; min-width:0; display:flex; align-items:center; gap:.5rem; padding:.3rem .4rem .3rem .75rem; border:1px solid rgba(112,168,228,.22); border-radius:.6rem; background:rgba(4,14,29,.5); color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; transition:border-color .14s, box-shadow .14s, background .14s; }
35
+ .agent-urlbar-input:focus-within { border-color:rgba(84,183,255,.55); box-shadow:0 0 0 2px rgba(84,183,255,.14); background:rgba(8,25,48,.6); }
36
+ .agent-urlbar-lock { flex:0 0 auto; color:#5ef0b3; font-size:.7rem; line-height:1; }
37
+ .agent-urlbar-field { flex:1; min-width:0; border:0; outline:none; color:#dcecff; background:transparent; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; caret-color:#54b7ff; }
38
+ .agent-urlbar-field::placeholder { color:#4b647f; }
39
+ .agent-urlbar-btn { display:grid; place-items:center; flex:0 0 auto; width:1.7rem; height:1.7rem; border:1px solid rgba(112,168,228,.24); border-radius:.5rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.78rem; line-height:1; cursor:pointer; transition:border-color .14s, color .14s, background .14s, transform .14s; }
40
+ .agent-urlbar-btn:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px); }
41
+ .agent-urlbar-btn.copied { color:#6df0bb; border-color:rgba(52,211,153,.5); background:rgba(16,185,129,.14); }
42
+ .agent-urlbar-note { flex:0 0 auto; color:#4d6378; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; letter-spacing:.04em; }
43
+ .agent-header .agent-select-wrap { min-width:0; }
44
+ .agent-header .agent-select { min-width:0; text-overflow:ellipsis; }
45
+ .agent-back { display:inline-flex; align-items:center; gap:.45rem; padding:.5rem .8rem; border:1px solid rgba(84,183,255,.34); border-radius:.6rem; color:#9fd4ff; background:rgba(84,183,255,.1); font-size:.68rem; font-weight:700; letter-spacing:.02em; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s; }
46
+ .agent-back:hover { border-color:rgba(84,183,255,.7); color:#fff; background:rgba(84,183,255,.2); transform:translateY(-1px); }
47
+ .agent-brand { display:inline-flex; align-items:center; gap:.75rem; margin:0; }
48
+ .agent-brand-badge { position:relative; display:grid; place-items:center; width:2.25rem; height:2.25rem; border-radius:.7rem; border:1px solid rgba(84,183,255,.42); background:linear-gradient(135deg, rgba(38,130,225,.32), rgba(90,67,183,.26)); box-shadow:0 0 20px rgba(58,149,255,.32), inset 0 1px rgba(255,255,255,.16); }
49
+ .agent-brand-badge::after { position:absolute; inset:0; border-radius:.7rem; content:""; background:radial-gradient(120% 120% at 50% 0%, rgba(120,200,255,.28), transparent 55%); }
50
+ .agent-brand-dot { position:relative; z-index:1; width:.52rem; height:.52rem; border-radius:50%; background:#5ef0b3; box-shadow:0 0 12px rgba(94,240,179,.95), 0 0 26px rgba(94,240,179,.45); }
51
+ .agent-brand-text { display:flex; flex-direction:column; gap:.12rem; line-height:1.1; }
52
+ .agent-brand-name { color:#f1f8ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.8rem; font-weight:800; letter-spacing:.16em; text-transform:uppercase; }
53
+ .agent-brand-name em { color:#63bdff; font-style:normal; text-shadow:0 0 18px rgba(84,183,255,.5); }
54
+ .agent-brand-sub { color:#4f6b8c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.55rem; letter-spacing:.14em; text-transform:uppercase; }
55
+ .agent-spacer { flex:1; }
56
+ .agent-status { display:inline-flex; align-items:center; gap:.42rem; padding:.4rem .7rem; border:1px solid rgba(52,211,153,.32); border-radius:999px; background:rgba(16,185,129,.12); color:#7ee8bd; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; }
57
+ .agent-status-dot { width:.42rem; height:.42rem; border-radius:50%; background:#5ef0b3; box-shadow:0 0 10px rgba(94,240,179,.9); animation:agent-pulse 2.4s ease-in-out infinite; }
58
+ .agent-select-wrap { display:inline-flex; align-items:center; gap:.42rem; padding:.22rem .3rem .22rem .6rem; border:1px solid rgba(112,168,228,.2); border-radius:.6rem; background:rgba(20,46,79,.3); transition:border-color .14s, background .14s, box-shadow .14s; }
59
+ .agent-select-wrap:focus-within { border-color:rgba(84,183,255,.55); background:rgba(20,46,79,.5); box-shadow:0 0 0 2px rgba(84,183,255,.15); }
60
+ .agent-select-label { color:#53708f; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.54rem; letter-spacing:.13em; text-transform:uppercase; white-space:nowrap; }
61
+ .agent-select { appearance:auto; -webkit-appearance:auto; padding:.42rem .55rem; border:0; border-radius:.4rem; outline:none; color:#dcecff; background:#0e1b33; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; font-weight:500; line-height:1.3; max-width:220px; min-height:1.9rem; cursor:pointer; color-scheme:dark; transition:background .14s, box-shadow .14s; }
62
+ .agent-select:hover { background:#132a4d; }
63
+ .agent-select:focus-visible { box-shadow:0 0 0 2px rgba(84,183,255,.35); }
64
+ .agent-select option { background:#0b1024; color:#e5f4ff; padding:.4rem .6rem; font-size:.64rem; }
65
+ .agent-select option:checked { background:#16325c; color:#fff; }
66
+ .agent-select optgroup { background:#0b1024; color:#63bdff; font-style:normal; font-size:.6rem; padding:.3rem .5rem; }
67
+ .agent-select optgroup option { background:#0b1024; color:#dcecff; font-style:normal; }
68
+ .agent-select optgroup option:hover { background:#1c3f6e; color:#fff; }
69
+ .agent-actions { display:inline-flex; align-items:center; gap:.45rem; }
70
+ .agent-clear { display:inline-flex; align-items:center; gap:.4rem; padding:.5rem .75rem; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.66rem; font-weight:700; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s; }
71
+ .agent-clear:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px); }
72
+ .agent-clear.danger { border-color:rgba(251,113,133,.3); color:#f8c0cd; background:rgba(244,63,94,.1); }
73
+ .agent-clear.danger:hover { border-color:rgba(251,113,133,.55); color:#ffe4e6; background:rgba(244,63,94,.24); }
74
+ .agent-modes { display:inline-flex; gap:.25rem; padding:.25rem; border:1px solid rgba(112,168,228,.2); border-radius:.6rem; background:rgba(20,46,79,.28); }
75
+ .agent-mode { padding:.42rem .7rem; border:1px solid transparent; border-radius:.45rem; color:#7d94b2; background:transparent; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:700; letter-spacing:.08em; cursor:pointer; transition:color .14s, background .14s, border-color .14s, box-shadow .14s; }
76
+ .agent-mode:hover { color:#dcecff; }
77
+ .agent-mode.mode-plan.active { color:#ffd479; background:linear-gradient(135deg, rgba(245,158,11,.28), rgba(180,83,9,.16)); border-color:rgba(255,196,107,.4); box-shadow:0 0 14px rgba(245,158,11,.2); }
78
+ .agent-mode.mode-auto.active { color:#6df0bb; background:linear-gradient(135deg, rgba(16,185,129,.26), rgba(6,95,70,.16)); border-color:rgba(52,211,153,.4); box-shadow:0 0 14px rgba(16,185,129,.2); }
79
+ .agent-mode.mode-build.active { color:#bfe9ff; background:linear-gradient(135deg, rgba(40,137,235,.34), rgba(76,83,212,.22)); border-color:rgba(84,183,255,.5); box-shadow:0 0 16px rgba(84,183,255,.24), inset 0 1px rgba(255,255,255,.08); }
80
+
81
+ .agent-workspace { flex:1 1 auto; min-height:0; min-width:0; display:flex; width:calc(100% - 48px); max-width:1400px; margin:0 auto; }
82
+ .agent-main { flex:1 1 auto; min-width:0; min-height:0; overflow-y:auto; overflow-x:hidden; padding:2rem 1.75rem 1.5rem; }
83
+ .agent-side { flex:0 0 360px; min-width:0; min-height:0; overflow-y:auto; overflow-x:hidden; border-left:1px solid rgba(112,168,228,.13); background:rgba(4,15,31,.4); padding:1.4rem 0 1.3rem 1.2rem; }
84
+ .agent-side-backdrop { position:fixed; inset:0; z-index:45; background:rgba(2,8,20,.5); backdrop-filter:blur(2px); -webkit-backdrop-filter:blur(2px); animation:agent-fade .2s ease both; }
85
+ @keyframes agent-fade { from { opacity:0; } to { opacity:1; } }
86
+ .agent-side-toggle { display:none; grid:place-items:center / none; place-items:center; width:2.1rem; height:2.1rem; padding:0; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.85rem; line-height:1; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s; }
87
+ .agent-side-toggle:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); }
88
+ .agent-side-toggle.on { color:#9fe8ff; border-color:rgba(99,189,255,.6); background:rgba(40,137,235,.2); }
89
+ .agent-side-inner { display:flex; flex-direction:column; gap:1.1rem; }
90
+ .agent-side-head { display:flex; align-items:center; gap:.5rem; color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; letter-spacing:.13em; }
91
+ .agent-side-title { font-weight:800; color:#63bdff; }
92
+ .agent-side-badge { padding:.08rem .45rem; border-radius:999px; color:#ffd479; background:rgba(245,158,11,.16); border:1px solid rgba(255,196,107,.3); font-size:.6rem; font-weight:800; }
93
+ .agent-side-batch { display:flex; gap:.4rem; }
94
+ .agent-side-batch-btn { padding:.34rem .6rem; border-radius:.45rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; font-weight:700; cursor:pointer; transition:filter .14s, transform .14s; }
95
+ .agent-side-batch-btn.allow { border:1px solid rgba(52,211,153,.4); color:#a7f3d0; background:linear-gradient(135deg, rgba(16,185,129,.55), rgba(5,150,105,.45)); }
96
+ .agent-side-batch-btn.deny { border:1px solid rgba(251,113,133,.4); color:#fecdd3; background:linear-gradient(135deg, rgba(244,63,94,.5), rgba(159,18,57,.42)); }
97
+ .agent-side-batch-btn:hover { filter:brightness(1.14); transform:translateY(-1px); }
98
+ .agent-side-section { display:flex; flex-direction:column; gap:.4rem; }
99
+ .agent-side-section-head { display:flex; align-items:center; gap:.4rem; }
100
+ .agent-side-section-title { color:#536c8c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; letter-spacing:.16em; text-transform:uppercase; }
101
+ .agent-side-section-badge { padding:.05rem .42rem; border-radius:999px; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:800; line-height:1.4; border:1px solid; }
102
+ .agent-side-section-badge.approval { color:#ffd479; border-color:rgba(255,196,107,.32); background:rgba(245,158,11,.14); }
103
+ .agent-side-section-badge.running { color:#8ed6ff; border-color:rgba(84,183,255,.34); background:rgba(84,183,255,.12); }
104
+ .agent-side-section-badge.done { color:#6df0bb; border-color:rgba(52,211,153,.3); background:rgba(16,185,129,.12); }
105
+ .agent-side-section-badge.zero { opacity:.45; }
106
+ .agent-side-empty { margin:0; color:#45597a; font-size:.64rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; }
107
+ .agent-task-row { display:flex; align-items:center; gap:.45rem; padding:.4rem .55rem; border:1px solid rgba(112,168,228,.17); border-radius:.55rem; background:rgba(8,25,48,.6); box-shadow:inset 0 1px rgba(255,255,255,.04); cursor:pointer; transition:border-color .14s, background .14s, transform .14s; }
108
+ .agent-task-row:hover { border-color:rgba(84,183,255,.5); background:rgba(84,183,255,.1); transform:translateY(-1px); }
109
+ .agent-task-dot { width:.4rem; height:.4rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); }
110
+ .agent-task-dot.running { animation:agent-pulse 1s ease-in-out infinite; }
111
+ .agent-task-dot.ok, .agent-task-dot.approved { background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.8); }
112
+ .agent-task-dot.error, .agent-task-dot.denied { background:#fb7185; box-shadow:0 0 8px rgba(244,63,94,.7); }
113
+ .agent-task-dot.approval { background:#fbbf24; box-shadow:0 0 8px rgba(251,191,36,.7); animation:agent-pulse 1s ease-in-out infinite; }
114
+ .agent-task-summary { flex:1; min-width:0; color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
115
+ .agent-task-actions { display:inline-flex; gap:.3rem; flex:0 0 auto; }
116
+ .agent-task-allow, .agent-task-deny { padding:.2rem .45rem; border-radius:.4rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:700; cursor:pointer; transition:filter .14s; }
117
+ .agent-task-allow { border:1px solid rgba(52,211,153,.4); color:#a7f3d0; background:rgba(16,185,129,.22); }
118
+ .agent-task-deny { border:1px solid rgba(251,113,133,.4); color:#fecdd3; background:rgba(244,63,94,.22); }
119
+ .agent-task-allow:hover, .agent-task-deny:hover { filter:brightness(1.15); }
120
+ .agent-list { display:flex; flex-direction:column; gap:1rem; }
121
+ .agent-empty { margin:3.5rem 0 0; color:#64758e; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.72rem; line-height:1.9; text-align:center; }
122
+ .agent-empty b { color:#54b7ff; font-weight:600; }
123
+ .agent-hint { display:block; margin-top:.6rem; font-size:.6rem; color:#4c5d77; line-height:1.9; }
124
+ .agent-hint b { color:#7c8fa9; font-weight:600; }
125
+ .agent-approval-actions { display:flex; gap:.5rem; padding:.6rem .85rem; border-top:1px solid rgba(112,168,228,.12); }
126
+ .agent-allow { padding:.45rem .85rem; border:1px solid rgba(52,211,153,.4); border-radius:.5rem; color:#a7f3d0; background:linear-gradient(135deg, rgba(16,185,129,.6), rgba(5,150,105,.5)); font-size:.68rem; font-weight:700; cursor:pointer; transition:filter .15s, transform .15s; }
127
+ .agent-allow:hover { filter:brightness(1.12); transform:translateY(-1px); }
128
+ .agent-deny { padding:.45rem .85rem; border:1px solid rgba(251,113,133,.4); border-radius:.5rem; color:#fecdd3; background:linear-gradient(135deg, rgba(244,63,94,.55), rgba(159,18,57,.48)); font-size:.68rem; font-weight:700; cursor:pointer; transition:filter .15s, transform .15s; }
129
+ .agent-deny:hover { filter:brightness(1.12); transform:translateY(-1px); }
130
+
131
+ .agent-row-user { display:flex; justify-content:flex-end; }
132
+ .agent-row-agent { display:flex; justify-content:flex-start; }
133
+ .agent-msg { display:flex; flex-direction:column; gap:.35rem; max-width:88%; min-width:0; }
134
+ .agent-role { display:inline-flex; align-items:center; gap:.45rem; color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; letter-spacing:.13em; text-transform:uppercase; }
135
+ .agent-role-user { justify-content:flex-end; }
136
+ .agent-role-agent { justify-content:flex-start; }
137
+ .agent-bubble { padding:.75rem .95rem; border:1px solid rgba(112,168,228,.2); border-radius:.8rem; color:#dcecff; background:linear-gradient(145deg, rgba(13,35,67,.5), rgba(10,17,40,.5)); font-size:.86rem; line-height:1.7; white-space:pre-wrap; overflow-wrap:anywhere; word-break:break-word; box-shadow:0 14px 42px rgba(0,0,0,.14); }
138
+ .agent-row-user .agent-bubble { border-color:rgba(84,183,255,.4); background:linear-gradient(145deg, rgba(22,70,122,.5), rgba(46,41,112,.42)); color:#eaf7ff; box-shadow:inset 0 1px rgba(255,255,255,.06), 0 14px 42px rgba(0,0,0,.16); }
139
+ .agent-msg pre { margin:0; }
140
+
141
+ .agent-tool { border:1px solid rgba(112,168,228,.2); border-radius:.8rem; background:rgba(8,25,48,.7); box-shadow:inset 0 1px rgba(255,255,255,.04), 0 14px 42px rgba(0,0,0,.16); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; overflow:hidden; backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); }
142
+ .agent-tool-head { display:flex; align-items:center; gap:.5rem; padding:.5rem .8rem; border-bottom:1px solid rgba(112,168,228,.12); color:#e2e8f0; font-size:.7rem; cursor:pointer; user-select:none; flex-wrap:wrap; }
143
+ .agent-tool-head .agent-tool-name { flex:0 0 auto; }
144
+ .agent-tool-head .agent-tool-summary { min-width:80px; }
145
+ .agent-tool-dot { width:.42rem; height:.42rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 10px rgba(84,183,255,.8); animation:agent-pulse 1s ease-in-out infinite; }
146
+ @keyframes agent-pulse { 0%,100% { opacity:1; } 50% { opacity:.3; } }
147
+ .agent-tool.ok .agent-tool-dot { background:#5ef0b3; box-shadow:0 0 10px rgba(94,240,179,.8); animation:none; }
148
+ .agent-tool.err .agent-tool-dot { background:#fb7185; box-shadow:0 0 10px rgba(244,63,94,.8); animation:none; }
149
+ .agent-tool-name { color:#5cbaff; font-weight:700; flex:0 0 auto; }
150
+ .agent-tool.err .agent-tool-name { color:#fda4af; }
151
+ .agent-tool-summary { flex:1; min-width:0; color:#cfe3fb; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; letter-spacing:.01em; }
152
+ .agent-tool.err .agent-tool-summary { color:#fda4af; }
153
+ .agent-tool-status { flex:0 0 auto; padding:.12rem .42rem; border-radius:.35rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.52rem; font-weight:800; letter-spacing:.06em; text-transform:uppercase; border:1px solid transparent; }
154
+ .agent-tool-status.running { color:#8ed6ff; background:rgba(84,183,255,.12); border-color:rgba(84,183,255,.25); }
155
+ .agent-tool-status.ok { color:#6df0bb; background:rgba(16,185,129,.12); border-color:rgba(52,211,153,.22); }
156
+ .agent-tool-status.err { color:#fda4af; background:rgba(244,63,94,.12); border-color:rgba(244,63,94,.22); }
157
+ .agent-tool-badge { display:inline-flex; align-items:center; gap:.28rem; padding:.18rem .46rem; border-radius:.4rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.54rem; font-weight:800; letter-spacing:.07em; text-transform:uppercase; border:1px solid; flex:0 0 auto; line-height:1; }
158
+ .agent-tool-badge i { font-style:normal; font-size:.62rem; opacity:.9; }
159
+ .badge-read { color:#8ed6ff; border-color:rgba(84,183,255,.32); background:rgba(84,183,255,.14); }
160
+ .badge-write { color:#c4b5fd; border-color:rgba(139,92,246,.32); background:rgba(139,92,246,.13); }
161
+ .badge-edit { color:#6df0bb; border-color:rgba(52,211,153,.32); background:rgba(16,185,129,.14); }
162
+ .badge-bash { color:#fcd34d; border-color:rgba(245,158,11,.3); background:rgba(245,158,11,.13); }
163
+ .badge-grep { color:#f59e0b; border-color:rgba(245,158,11,.22); background:rgba(245,158,11,.08); }
164
+ .badge-glob { color:#93c5fd; border-color:rgba(96,165,250,.22); background:rgba(59,130,246,.08); }
165
+ .badge-list { color:#94a3b8; border-color:rgba(148,163,184,.22); background:rgba(148,163,184,.08); }
166
+ .badge-search { color:#67e8f9; border-color:rgba(6,182,212,.22); background:rgba(6,182,212,.08); }
167
+ .badge-generic { color:#9fd4ff; border-color:rgba(84,183,255,.22); background:rgba(84,183,255,.08); }
168
+
169
+ .agent-badge { display:flex; flex-direction:column; border:1px solid rgba(112,168,228,.22); border-radius:999px; background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.58)); box-shadow:inset 0 1px rgba(255,255,255,.05), 0 10px 26px rgba(0,0,0,.2); overflow:hidden; animation:agent-rise .28s cubic-bezier(.22,1,.36,1) both; transition:border-color .15s, border-radius .15s, transform .15s; }
170
+ .agent-badge:hover { border-color:rgba(84,183,255,.45); transform:translateY(-1px); }
171
+ .agent-badge-head { display:flex; align-items:center; gap:.5rem; padding:.42rem .8rem; color:#e2e8f0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.7rem; cursor:pointer; user-select:none; }
172
+ .agent-badge-dot { width:.42rem; height:.42rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); animation:agent-pulse 1s ease-in-out infinite; }
173
+ .agent-badge.ok .agent-badge-dot { background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.7); animation:none; }
174
+ .agent-badge.err .agent-badge-dot { background:#fb7185; box-shadow:0 0 8px rgba(244,63,94,.7); animation:none; }
175
+ .agent-badge-name { color:#9fd4ff; font-weight:700; flex:0 0 auto; letter-spacing:.04em; }
176
+ .agent-badge.err .agent-badge-name { color:#fda4af; }
177
+ .agent-badge-param { flex:1; min-width:0; color:#9fb3d0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
178
+ .agent-badge.err .agent-badge-param { color:#fda4af; }
179
+ .agent-badge.expanded { border-radius:.85rem; }
180
+ .agent-toggle { display:inline-flex; align-items:center; justify-content:center; width:1.35rem; height:1.35rem; flex:0 0 auto; border:1px solid rgba(112,168,228,.22); border-radius:.4rem; color:#8fb8e4; background:transparent; font-family:inherit; font-size:.6rem; line-height:1; cursor:pointer; transition:border-color .14s, color .14s, background .14s; }
181
+ .agent-toggle:hover { border-color:rgba(84,183,255,.5); color:#bfe9ff; background:rgba(84,183,255,.1); }
182
+ .agent-tool-code { position:relative; border-top:1px solid rgba(112,168,228,.1); background:rgba(0,0,0,.3); }
183
+ .agent-tool-code .agent-tool-output { border-top:0; padding-top:2rem; }
184
+ .agent-copy { position:absolute; top:.5rem; right:.6rem; z-index:2; padding:.22rem .5rem; border:1px solid rgba(112,168,228,.28); border-radius:.4rem; color:#8fb8e4; background:rgba(4,14,29,.8); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; cursor:pointer; transition:border-color .14s, color .14s; }
185
+ .agent-copy:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; }
186
+ .agent-diff { max-height:280px; overflow:auto; padding:2.2rem .6rem .6rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.7rem; line-height:1.6; }
187
+ .diff-line { display:flex; gap:.4rem; padding:.05rem 0; white-space:pre-wrap; word-break:break-word; }
188
+ .diff-marker { width:1ch; flex:0 0 auto; opacity:.7; }
189
+ .diff-line.diff-hunk { color:#67c8ff; background:rgba(84,183,255,.1); }
190
+ .diff-line.diff-add { color:#6df0bb; background:rgba(16,185,129,.16); }
191
+ .diff-line.diff-del { color:#fda4af; background:rgba(244,63,94,.16); }
192
+ .diff-line.diff-meta { color:#718ba7; }
193
+ .agent-timer { display:flex; align-items:center; gap:.45rem; padding:.4rem .85rem 0; }
194
+ .agent-timer-bar { height:.28rem; flex:0 0 auto; border-radius:999px; background:linear-gradient(90deg,#fbbf24,#f59e0b); box-shadow:0 0 8px rgba(245,158,11,.35); transition:width .25s linear; }
195
+ .agent-timer-label { color:#ffd479; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; }
196
+ .agent-tool-out { margin:0; padding:.55rem .85rem; border-top:1px solid rgba(112,168,228,.14); background:rgba(255,255,255,.035); color:#a6bfd8; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.72rem; line-height:1.65; white-space:pre-wrap; word-break:break-word; }
197
+ .agent-tool-output { margin:0; max-height:280px; overflow:auto; padding:.85rem .95rem; color:#e8f2ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.82rem; line-height:1.75; white-space:pre-wrap; word-break:break-word; }
198
+ .agent-tool-output .hljs { display:block; background:transparent; color:#e8f2ff; }
199
+ .agent-tool-output .hljs-keyword, .agent-tool-output .hljs-selector-tag, .agent-tool-output .hljs-meta .hljs-keyword { color:#c792ea; }
200
+ .agent-tool-output .hljs-string, .agent-tool-output .hljs-attr, .agent-tool-output .hljs-regexp, .agent-tool-output .hljs-template-string { color:#7fe0a8; }
201
+ .agent-tool-output .hljs-number, .agent-tool-output .hljs-literal, .agent-tool-output .hljs-symbol, .agent-tool-output .hljs-bullet, .agent-tool-output .hljs-params { color:#e2a76f; }
202
+ .agent-tool-output .hljs-comment, .agent-tool-output .hljs-quote { color:#6b85a3; font-style:italic; }
203
+ .agent-tool-output .hljs-title, .agent-tool-output .hljs-title.function_, .agent-tool-output .hljs-function .hljs-title, .agent-tool-output .hljs-section { color:#82c6ff; }
204
+ .agent-tool-output .hljs-built_in, .agent-tool-output .hljs-selector-class, .agent-tool-output .hljs-selector-id { color:#e5c07b; }
205
+ .agent-tool-output .hljs-tag, .agent-tool-output .hljs-name, .agent-tool-output .hljs-variable, .agent-tool-output .hljs-template-variable { color:#f18ac6; }
206
+ .agent-tool-output .hljs-meta, .agent-tool-output .hljs-doctag { color:#56b6c2; }
207
+
208
+ .agent-typing { display:inline-flex; align-items:center; gap:.4rem; color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; }
209
+ .agent-typing .agent-cursor { display:inline-block; width:.5rem; height:1rem; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); animation:agent-blink 1s steps(1) infinite; }
210
+ @keyframes agent-blink { 50% { opacity:0; } }
211
+
212
+ .agent-error { width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.5rem .2rem 0; color:#fda4af; font-size:.74rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; }
213
+ .agent-notice { width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.5rem .2rem 0; color:#8fa5c0; font-size:.7rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-style:italic; }
214
+ .agent-footer { position:relative; z-index:20; flex:0 0 auto; border-top:1px solid rgba(112,168,228,.15); background:rgba(4,14,29,.62); backdrop-filter:blur(24px); -webkit-backdrop-filter:blur(24px); }
215
+ .agent-footer-bar { display:flex; align-items:center; gap:.9rem; flex-wrap:wrap; width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.6rem 0 .15rem; }
216
+ .agent-footer-hint { margin-left:auto; color:#4d6378; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; letter-spacing:.04em; }
217
+ .agent-footer-inner { display:flex; align-items:flex-end; gap:.7rem; width:calc(100% - 48px); max-width:1400px; margin:0 auto; padding:.7rem 0 .95rem; }
218
+ .agent-input { flex:1; padding:.7rem .85rem; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; outline:none; resize:none; color:#e8f2ff; background:rgba(255,255,255,.05); font:inherit; font-size:.84rem; line-height:1.55; transition:border-color .14s, box-shadow .14s, background .14s; }
219
+ .agent-input::placeholder { color:#5b7392; }
220
+ .agent-input:focus { border-color:rgba(84,183,255,.6); box-shadow:0 0 0 3px rgba(84,183,255,.14); background:rgba(255,255,255,.08); }
221
+ .agent-send { padding:.7rem 1.15rem; border:1px solid rgba(84,183,255,.55); border-radius:.6rem; color:#fff; background:linear-gradient(135deg, rgba(38,130,225,.75), rgba(90,67,183,.65)); box-shadow:0 8px 24px rgba(84,183,255,.28); font-size:.8rem; font-weight:800; cursor:pointer; transition:transform .15s, filter .15s, opacity .15s; }
222
+ .agent-send:hover:not(:disabled) { filter:brightness(1.1); transform:translateY(-1px); }
223
+ .agent-send:disabled { opacity:.4; cursor:not-allowed; }
224
+
225
+ /* ── Redesign additions — aurora glass, animations, todos, hero ─────── */
226
+ .agent-mode:active { transform:scale(.95); }
227
+ .agent-tool { background:linear-gradient(160deg, rgba(16,38,70,.55), rgba(8,20,42,.62)); border-color:rgba(112,168,228,.24); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 18px 50px rgba(0,0,0,.28); backdrop-filter:blur(20px) saturate(140%); -webkit-backdrop-filter:blur(20px) saturate(140%); animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both; transition:border-color .16s, transform .16s, box-shadow .16s; }
228
+ .agent-tool:hover { transform:translateY(-2px); border-color:rgba(84,183,255,.4); }
229
+ .agent-row-user, .agent-row-agent { animation:agent-rise .28s cubic-bezier(.22,1,.36,1) both; }
230
+ .agent-bubble { background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.56)); border-color:rgba(112,168,228,.22); box-shadow:inset 0 1px rgba(255,255,255,.05), 0 18px 50px rgba(0,0,0,.24); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%); }
231
+ .agent-row-user .agent-bubble { background:linear-gradient(160deg, rgba(22,70,122,.58), rgba(46,41,112,.5)); border-color:rgba(84,183,255,.42); }
232
+ .agent-tool-body { animation:agent-open .18s cubic-bezier(.22,1,.36,1); }
233
+ @keyframes agent-rise { from { opacity:0; transform:translateY(10px); filter:blur(2px); } to { opacity:1; transform:translateY(0); filter:blur(0); } }
234
+ @keyframes agent-open { from { opacity:0; transform:translateY(-4px); } to { opacity:1; transform:translateY(0); } }
235
+
236
+ .agent-side-section-head { cursor:pointer; user-select:none; transition:color .12s; }
237
+ .agent-side-section-head:hover { color:#dcecff; }
238
+ .agent-side-chevron { display:inline-block; font-size:.58rem; color:#45597a; transition:transform .16s; }
239
+ .agent-side-chevron.collapsed { transform:rotate(-90deg); }
240
+
241
+ .agent-todos-add { display:flex; gap:.4rem; }
242
+ .agent-todos-input { flex:1; min-width:0; padding:.42rem .55rem; border:1px solid rgba(112,168,228,.22); border-radius:.5rem; outline:none; color:#e5f4ff; background:rgba(20,46,79,.32); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; transition:border-color .14s, box-shadow .14s; }
243
+ .agent-todos-input::placeholder { color:#536c8c; }
244
+ .agent-todos-input:focus { border-color:rgba(84,183,255,.55); box-shadow:0 0 0 2px rgba(84,183,255,.15); }
245
+ .agent-todos-add-btn { flex:0 0 auto; width:1.9rem; border:1px solid rgba(84,183,255,.4); border-radius:.5rem; color:#bfe9ff; background:linear-gradient(135deg, rgba(38,130,225,.5), rgba(90,67,183,.45)); font-size:1rem; line-height:1; cursor:pointer; transition:filter .14s, transform .14s; }
246
+ .agent-todos-add-btn:hover { filter:brightness(1.15); }
247
+ .agent-todos-add-btn:active { transform:scale(.94); }
248
+ .agent-todos-add-btn:disabled { opacity:.4; cursor:not-allowed; }
249
+ .agent-todos-progress { display:flex; align-items:center; gap:.45rem; }
250
+ .agent-todos-progress-bar { flex:0 0 auto; height:.24rem; border-radius:999px; background:linear-gradient(90deg, #3f9bff, #6cd9ff); box-shadow:0 0 8px rgba(84,183,255,.5); transition:width .25s ease; }
251
+ .agent-todos-progress span { color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; }
252
+ .agent-todo-row { display:flex; align-items:center; gap:.45rem; padding:.38rem .5rem; border:1px solid rgba(112,168,228,.16); border-radius:.5rem; background:rgba(8,25,48,.55); box-shadow:inset 0 1px rgba(255,255,255,.04); transition:border-color .14s, background .14s; }
253
+ .agent-todo-row:hover { border-color:rgba(84,183,255,.4); background:rgba(84,183,255,.08); }
254
+ .agent-todo-check { display:grid; place-items:center; width:1.05rem; height:1.05rem; flex:0 0 auto; border:1px solid rgba(112,168,228,.4); border-radius:.3rem; color:transparent; background:rgba(255,255,255,.05); font-size:.6rem; cursor:pointer; transition:all .14s; }
255
+ .agent-todo-check.on { border-color:rgba(52,211,153,.55); color:#0b241b; background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.5); }
256
+ .agent-todo-text { flex:1; min-width:0; color:#cfe3fb; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
257
+ .agent-todo-row.done .agent-todo-text { color:#5b7392; text-decoration:line-through; }
258
+ .agent-todo-del { flex:0 0 auto; width:1.2rem; height:1.2rem; border:0; border-radius:.3rem; color:#6e8aaa; background:transparent; font-size:.9rem; line-height:1; cursor:pointer; transition:color .12s, background .12s; }
259
+ .agent-todo-del:hover { color:#fecdd3; background:rgba(244,63,94,.16); }
260
+
261
+ .agent-hero { display:flex; flex-direction:column; align-items:center; gap:.6rem; margin:5vh auto 0; max-width:640px; padding:2.5rem 2rem; text-align:center; border:1px solid rgba(112,168,228,.2); border-radius:1.2rem; background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.55)); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 24px 70px rgba(0,0,0,.3); backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); animation:agent-rise .4s cubic-bezier(.22,1,.36,1) both; }
262
+ .agent-hero-orb { position:relative; display:grid; place-items:center; width:74px; height:74px; margin-bottom:.4rem; border:1px solid rgba(84,183,255,.35); border-radius:50%; background:radial-gradient(circle, rgba(57,170,255,.4), rgba(56,58,179,.16) 46%, transparent 70%); box-shadow:0 0 34px rgba(58,149,255,.35), inset 0 0 24px rgba(94,119,255,.18); animation:agent-orbit-spin 14s linear infinite; }
263
+ .agent-hero-orb::before { position:absolute; width:70%; height:26%; border:1px solid rgba(112,206,255,.6); border-radius:50%; content:""; transform:rotate(-28deg); }
264
+ .agent-hero-orb span { width:6px; height:6px; border-radius:50%; background:#5ef0b3; box-shadow:0 0 12px #5ef0b3; z-index:1; }
265
+ @keyframes agent-orbit-spin { to { transform:rotate(360deg); } }
266
+ .agent-hero h2 { margin:.4rem 0 0; color:#f1f8ff; font-size:1.5rem; letter-spacing:-.03em; }
267
+ .agent-hero h2 em { color:#6fcaff; font-style:normal; text-shadow:0 0 24px rgba(80,183,255,.4); }
268
+ .agent-hero p { margin:0; color:#8fa5c0; font-size:.78rem; line-height:1.7; }
269
+ .agent-hero-chips { display:flex; flex-wrap:wrap; justify-content:center; gap:.5rem; margin-top:.9rem; }
270
+ .agent-hero-chip { padding:.5rem .8rem; border:1px solid rgba(112,168,228,.28); border-radius:999px; color:#a8d8ff; background:rgba(84,183,255,.08); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; cursor:pointer; transition:border-color .14s, background .14s, transform .14s; }
271
+ .agent-hero-chip:hover { border-color:rgba(84,183,255,.6); background:rgba(84,183,255,.16); transform:translateY(-2px); }
272
+ .agent-hero .agent-hint { margin-top:.6rem; }
273
+
274
+ .agent-suggest { position:fixed; z-index:70; left:50%; bottom:4.4rem; transform:translateX(-50%); display:flex; flex-direction:column; gap:.55rem; width:min(560px, calc(100vw - 32px)); padding:.95rem 1.1rem; border:1px solid rgba(255,196,107,.38); border-radius:.9rem; background:linear-gradient(160deg, rgba(48,34,10,.92), rgba(26,18,40,.9)); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 24px 70px rgba(0,0,0,.5), 0 0 30px rgba(245,158,11,.14); backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); animation:agent-rise .25s cubic-bezier(.22,1,.36,1) both; }
275
+ .agent-suggest-close { position:absolute; top:.55rem; right:.55rem; width:1.4rem; height:1.4rem; display:grid; place-items:center; border:1px solid rgba(255,196,107,.28); border-radius:.4rem; color:#b98a3a; background:rgba(0,0,0,.25); font-size:.7rem; line-height:1; cursor:pointer; transition:color .14s, border-color .14s, background .14s; }
276
+ .agent-suggest-close:hover { color:#ffe4b3; border-color:rgba(255,196,107,.6); background:rgba(245,158,11,.2); }
277
+ .agent-suggest-head { display:flex; align-items:baseline; gap:.55rem; flex-wrap:wrap; padding-right:1.4rem; }
278
+ .agent-suggest-title { color:#ffd479; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; text-shadow:0 0 14px rgba(245,158,11,.35); }
279
+ .agent-suggest-sub { color:#8a7a58; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; }
280
+ .agent-suggest-chips { display:flex; flex-wrap:wrap; gap:.45rem; }
281
+ .agent-suggest-chips .agent-hero-chip { border-color:rgba(255,196,107,.38); color:#ffe3a3; background:rgba(245,158,11,.12); }
282
+ .agent-suggest-chips .agent-hero-chip:hover { border-color:rgba(255,196,107,.68); background:rgba(245,158,11,.22); transform:translateY(-2px); }
283
+
284
+ .agent-enhance { position:fixed; z-index:70; left:50%; bottom:4.4rem; transform:translateX(-50%); display:flex; flex-direction:column; gap:.6rem; width:min(560px, calc(100vw - 32px)); max-height:70vh; overflow:auto; padding:.95rem 1.1rem; border:1px solid rgba(99,189,255,.4); border-radius:.9rem; background:linear-gradient(160deg, rgba(14,42,78,.94), rgba(26,18,40,.92)); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 24px 70px rgba(0,0,0,.5), 0 0 30px rgba(84,183,255,.16); backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); animation:agent-rise .25s cubic-bezier(.22,1,.36,1) both; }
285
+ .agent-enhance-head { display:flex; align-items:baseline; gap:.55rem; flex-wrap:wrap; padding-right:1.4rem; }
286
+ .agent-enhance-title { color:#8ed6ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; text-shadow:0 0 14px rgba(84,183,255,.4); }
287
+ .agent-enhance-sub { color:#6d8fb2; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; }
288
+ .agent-enhance-input { width:100%; padding:.6rem .75rem; border:1px solid rgba(112,168,228,.26); border-radius:.6rem; outline:none; resize:vertical; color:#e8f2ff; background:rgba(255,255,255,.06); font:inherit; font-size:.8rem; line-height:1.55; transition:border-color .14s, box-shadow .14s; }
289
+ .agent-enhance-input:focus { border-color:rgba(99,189,255,.6); box-shadow:0 0 0 3px rgba(84,183,255,.14); }
290
+ .agent-enhance-label { color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; letter-spacing:.13em; text-transform:uppercase; }
291
+ .agent-enhance-bubble { padding:.7rem .85rem; border:1px solid rgba(112,168,228,.22); border-radius:.7rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.56)); font-size:.8rem; line-height:1.7; white-space:pre-wrap; overflow-wrap:anywhere; }
292
+ .agent-enhance-features { display:flex; flex-direction:column; gap:.5rem; }
293
+ .agent-enhance-chips { display:flex; flex-wrap:wrap; gap:.45rem; }
294
+ .agent-enhance-chip { padding:.42rem .7rem; border:1px solid rgba(112,168,228,.3); border-radius:999px; color:#a8d8ff; background:rgba(84,183,255,.08); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; cursor:pointer; transition:border-color .14s, background .14s, transform .14s; }
295
+ .agent-enhance-chip:hover { border-color:rgba(99,189,255,.6); background:rgba(84,183,255,.16); transform:translateY(-1px); }
296
+ .agent-enhance-chip.on { border-color:rgba(94,240,179,.55); color:#7ee8bd; background:rgba(16,185,129,.16); }
297
+ .agent-enhance-actions { display:flex; gap:.5rem; }
298
+ .agent-enhance-btn { padding:.5rem .95rem; border:1px solid rgba(99,189,255,.55); border-radius:.55rem; color:#eaf7ff; background:linear-gradient(135deg, rgba(38,130,225,.7), rgba(90,67,183,.6)); box-shadow:0 6px 18px rgba(84,183,255,.24); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; font-weight:800; cursor:pointer; transition:filter .15s, transform .15s, opacity .15s; }
299
+ .agent-enhance-btn:hover:not(:disabled) { filter:brightness(1.1); transform:translateY(-1px); }
300
+ .agent-enhance-btn:disabled { opacity:.45; cursor:not-allowed; }
301
+ .agent-enhance-btn.ghost { border-color:rgba(112,168,228,.3); color:#9fb3d0; background:rgba(84,183,255,.08); box-shadow:none; }
302
+ .agent-enhance-error { color:#fda4af; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; }
303
+ .agent-enhance-opt { display:flex; align-items:center; gap:.5rem; padding-top:.5rem; border-top:1px dashed rgba(112,168,228,.16); cursor:pointer; user-select:none; }
304
+ .agent-enhance-check { display:grid; place-items:center; width:1rem; height:1rem; flex:0 0 auto; border:1px solid rgba(112,168,228,.4); border-radius:.3rem; color:#0b241b; background:rgba(255,255,255,.05); font-size:.62rem; line-height:1; transition:border-color .14s, background .14s; }
305
+ .agent-enhance-check.on { border-color:rgba(94,240,179,.6); background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.45); }
306
+ .agent-enhance-opt-label { color:#7d94b2; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; }
307
+ .agent-enhance-toggle { display:inline-flex; align-items:center; gap:.42rem; padding:.4rem .7rem; border:1px solid rgba(112,168,228,.22); border-radius:.6rem; color:#9fd4ff; background:rgba(84,183,255,.08); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; cursor:pointer; transition:border-color .14s, color .14s, background .14s, box-shadow .14s, transform .14s; }
308
+ .agent-enhance-toggle:hover:not(:disabled) { color:#dcecff; border-color:rgba(99,189,255,.5); transform:translateY(-1px); }
309
+ .agent-enhance-toggle.on { color:#9fe8ff; border-color:rgba(99,189,255,.6); background:linear-gradient(135deg, rgba(40,137,235,.3), rgba(90,67,183,.22)); box-shadow:0 0 14px rgba(84,183,255,.25); }
310
+ .agent-enhance-toggle:disabled { opacity:.4; cursor:not-allowed; }
311
+
312
+ .agent-settings-btn { display:grid; place-items:center; width:2.1rem; height:2.1rem; padding:0; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.9rem; line-height:1; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s; }
313
+ .agent-settings-btn:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px); }
314
+ .agent-settings-btn.on { color:#9fe8ff; border-color:rgba(99,189,255,.6); background:linear-gradient(135deg, rgba(40,137,235,.3), rgba(90,67,183,.22)); box-shadow:0 0 14px rgba(84,183,255,.25); }
315
+
316
+ .agent-settings { position:fixed; z-index:65; top:0; right:0; bottom:0; width:min(340px, 92vw); display:flex; flex-direction:column; border-left:1px solid rgba(112,168,228,.2); background:linear-gradient(180deg, rgba(10,28,55,.97), rgba(6,15,34,.98)); box-shadow:-18px 0 60px rgba(0,0,0,.5); backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); animation:agent-fade .18s ease both; }
317
+ .agent-settings-head { display:flex; align-items:center; justify-content:space-between; padding:1rem 1.1rem; border-bottom:1px solid rgba(112,168,228,.14); }
318
+ .agent-settings-title { color:#8ed6ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; text-shadow:0 0 14px rgba(84,183,255,.4); }
319
+ .agent-settings-body { flex:1 1 auto; min-height:0; overflow-y:auto; padding:1rem 1.1rem 1.5rem; display:flex; flex-direction:column; gap:1.2rem; }
320
+ .agent-settings-section { display:flex; flex-direction:column; gap:.6rem; }
321
+ .agent-settings-row { display:flex; align-items:center; justify-content:space-between; gap:.5rem; padding:.55rem .7rem; border:1px solid rgba(112,168,228,.16); border-radius:.6rem; background:rgba(8,25,48,.55); cursor:pointer; user-select:none; transition:border-color .14s, background .14s; }
322
+ .agent-settings-row:hover { border-color:rgba(84,183,255,.4); background:rgba(84,183,255,.08); }
323
+ .agent-settings-row-label { color:#cfe3fb; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; }
324
+ .agent-settings-field { display:flex; flex-direction:column; gap:.3rem; }
325
+ .agent-settings-field span { color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; letter-spacing:.13em; text-transform:uppercase; }
326
+ .agent-settings-field .agent-select { width:100%; max-width:none; }
327
+ .agent-default-row { display:flex; align-items:center; gap:.5rem; margin-top:.2rem; }
328
+ .agent-default-btn { padding:.42rem .7rem; border:1px solid rgba(84,183,255,.34); border-radius:.5rem; color:#bfe9ff; background:linear-gradient(135deg, rgba(38,130,225,.28), rgba(90,67,183,.18)); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:800; letter-spacing:.06em; cursor:pointer; transition:filter .14s, border-color .14s, background .14s, opacity .14s; }
329
+ .agent-default-btn:hover:not(:disabled) { filter:brightness(1.15); border-color:rgba(84,183,255,.55); }
330
+ .agent-default-btn:disabled { opacity:.45; cursor:default; }
331
+ .agent-default-btn.is-default { color:#6df0bb; border-color:rgba(52,211,153,.32); background:rgba(16,185,129,.12); }
332
+ .agent-default-hint { color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; }
333
+
334
+ .agent-thinking { border:1px dashed rgba(112,168,228,.28); border-radius:.85rem; background:rgba(8,22,45,.4); overflow:hidden; animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both; }
335
+ .agent-thinking-head { display:flex; align-items:center; gap:.5rem; padding:.55rem .85rem; color:#8fa8c8; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; cursor:pointer; user-select:none; transition:background .14s; flex-wrap:wrap; }
336
+ .agent-thinking-head:hover { background:rgba(84,183,255,.07); }
337
+ .agent-thinking-dot { width:.42rem; height:.42rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.6); }
338
+ .agent-thinking-name { color:#9fd4ff; font-size:.62rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; }
339
+ .agent-thinking-summary { flex:0 1 auto; min-width:0; color:#5f7b9c; font-size:.6rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
340
+ .agent-thinking-current { display:inline-flex; align-items:center; gap:.3rem; flex:1 1 auto; min-width:0; padding:.14rem .42rem; border-radius:.4rem; background:rgba(84,183,255,.08); border:1px solid rgba(112,168,228,.14); font-size:.6rem; overflow:hidden; }
341
+ .agent-thinking-current-text { flex:1; min-width:0; color:#cfe3fb; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
342
+ .agent-thinking-current .agent-tool-badge { padding:.1rem .32rem; font-size:.48rem; }
343
+ .agent-thinking-live { color:#8ed6ff; font-size:.52rem; font-weight:800; animation:agent-pulse 1s ease-in-out infinite; }
344
+ .agent-thinking-more { flex:0 0 auto; padding:.14rem .46rem; border-radius:999px; color:#ffd479; background:rgba(245,158,11,.14); border:1px solid rgba(255,196,107,.3); font-size:.56rem; font-weight:800; }
345
+ .agent-thinking-preview { display:flex; align-items:center; gap:.4rem; padding:.42rem .75rem; border-top:1px dashed rgba(112,168,228,.16); background:rgba(8,25,48,.35); cursor:pointer; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; }
346
+ .agent-thinking-preview-dot { width:.34rem; height:.34rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 6px rgba(84,183,255,.6); animation:agent-pulse 1s ease-in-out infinite; }
347
+ .agent-thinking-preview-summary { flex:1; min-width:0; color:#cfe3fb; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
348
+ .agent-thinking-preview-status { flex:0 0 auto; padding:.1rem .36rem; border-radius:.3rem; font-size:.52rem; font-weight:800; text-transform:uppercase; }
349
+ .agent-thinking-preview-status.running { color:#8ed6ff; background:rgba(84,183,255,.12); border:1px solid rgba(84,183,255,.22); }
350
+ .agent-thinking-preview-status.ok { color:#6df0bb; background:rgba(16,185,129,.1); border:1px solid rgba(52,211,153,.18); }
351
+ .agent-thinking-preview-status.err { color:#fda4af; background:rgba(244,63,94,.1); border:1px solid rgba(244,63,94,.18); }
352
+ .agent-thinking-body { display:flex; flex-direction:column; gap:.5rem; padding:.6rem .85rem .85rem; border-top:1px dashed rgba(112,168,228,.16); }
353
+ .agent-thinking-body .agent-tool { box-shadow:none; }
354
+ .agent-thinking.collapsed .agent-thinking-head { background:rgba(8,25,48,.2); }
355
+
356
+ .agent-team { display:flex; flex-direction:column; gap:.6rem; padding:.85rem 1rem; border:1px solid rgba(84,183,255,.32); border-radius:.85rem; background:linear-gradient(160deg, rgba(16,38,70,.55), rgba(26,16,64,.42)); box-shadow:inset 0 1px rgba(255,255,255,.05), 0 16px 44px rgba(0,0,0,.24); backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px); animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both; }
357
+ .agent-team-head { display:flex; align-items:baseline; gap:.55rem; flex-wrap:wrap; }
358
+ .agent-team-title { color:#8ed6ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; text-shadow:0 0 14px rgba(84,183,255,.4); }
359
+ .agent-team-stage { color:#6d8fb2; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; }
360
+ .agent-team-grid { display:flex; flex-wrap:wrap; gap:.5rem; }
361
+ .agent-team-member { display:inline-flex; align-items:center; gap:.45rem; padding:.42rem .68rem; border:1px solid rgba(112,168,228,.18); border-radius:999px; background:rgba(8,25,48,.6); color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:600; transition:border-color .14s, color .14s; }
362
+ .agent-team-dot { width:.4rem; height:.4rem; flex:0 0 auto; border-radius:50%; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); animation:agent-pulse 1s ease-in-out infinite; }
363
+ .agent-team-member.done { color:#7ee8bd; border-color:rgba(52,211,153,.3); }
364
+ .agent-team-member.done .agent-team-dot { background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.8); animation:none; }
365
+ .agent-team-member.pending { opacity:.55; }
366
+ .agent-team-member.pending .agent-team-dot { animation:none; opacity:.4; }
367
+ .agent-team-spinner { width:.5rem; height:.5rem; flex:0 0 auto; border:2px solid rgba(84,183,255,.25); border-top-color:#54b7ff; border-radius:50%; animation:agent-spin .8s linear infinite; }
368
+ @keyframes agent-spin { to { transform:rotate(360deg); } }
369
+ .agent-team-toggle { display:inline-flex; align-items:center; gap:.42rem; padding:.4rem .7rem; border:1px solid rgba(112,168,228,.22); border-radius:.6rem; color:#7d94b2; background:rgba(20,46,79,.28); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:700; letter-spacing:.1em; text-transform:uppercase; cursor:pointer; transition:border-color .14s, color .14s, background .14s, box-shadow .14s, transform .14s; }
370
+ .agent-team-toggle:hover { color:#dcecff; border-color:rgba(84,183,255,.5); transform:translateY(-1px); }
371
+ .agent-team-toggle.on { color:#9fe8ff; border-color:rgba(84,183,255,.55); background:linear-gradient(135deg, rgba(40,137,235,.3), rgba(90,67,183,.22)); box-shadow:0 0 14px rgba(84,183,255,.25); }
372
+ .agent-team-toggle-dot { width:.4rem; height:.4rem; border-radius:50%; background:#4b647f; }
373
+ .agent-team-toggle.on .agent-team-toggle-dot { background:#5ef0b3; box-shadow:0 0 8px rgba(94,240,179,.9); }
374
+
375
+ .agent-typing { display:inline-flex; align-items:center; gap:.55rem; padding:.4rem .7rem; border:1px solid rgba(112,168,228,.16); border-radius:999px; color:#6e8aaa; background:rgba(8,25,48,.5); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; }
376
+ .agent-typing-label { letter-spacing:.08em; text-transform:uppercase; font-size:.56rem; }
377
+ .agent-dots { display:inline-flex; gap:.24rem; }
378
+ .agent-dots i { width:.32rem; height:.32rem; border-radius:50%; background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); animation:agent-bounce 1.2s ease-in-out infinite; }
379
+ .agent-dots i:nth-child(2) { animation-delay:.16s; }
380
+ .agent-dots i:nth-child(3) { animation-delay:.32s; }
381
+ @keyframes agent-bounce { 0%,60%,100% { transform:translateY(0); opacity:.5; } 30% { transform:translateY(-4px); opacity:1; } }
382
+
383
+ .agent-toast { position:fixed; top:4.5rem; right:1.5rem; z-index:60; padding:.6rem .9rem; border:1px solid rgba(84,183,255,.35); border-radius:.6rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.85), rgba(8,20,42,.9)); box-shadow:0 14px 40px rgba(0,0,0,.4); backdrop-filter:blur(20px) saturate(140%); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; animation:agent-open .22s cubic-bezier(.22,1,.36,1); }
384
+ .agent-scroll-down { position:fixed; right:1.6rem; bottom:6.5rem; z-index:55; display:grid; place-items:center; width:2.5rem; height:2.5rem; border:1px solid rgba(84,183,255,.4); border-radius:50%; color:#bfe9ff; background:linear-gradient(160deg, rgba(16,38,70,.85), rgba(8,20,42,.9)); box-shadow:0 10px 30px rgba(0,0,0,.35), 0 0 16px rgba(84,183,255,.2); backdrop-filter:blur(18px); font-size:1rem; cursor:pointer; transition:transform .14s, filter .14s; animation:agent-rise .2s cubic-bezier(.22,1,.36,1) both; }
385
+ .agent-scroll-down:hover { filter:brightness(1.15); transform:translateY(-2px); }
386
+ .agent-scroll-down:active { transform:scale(.92); }
387
+
388
+ .agent-explorer { position:fixed; inset:0; z-index:80; display:flex; align-items:center; justify-content:center; padding:1rem; background:rgba(2,8,20,.62); backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); animation:agent-fade .18s ease both; }
389
+ .agent-explorer-card { display:flex; flex-direction:column; width:min(600px, 100%); max-height:78vh; border:1px solid rgba(99,189,255,.28); border-radius:.9rem; background:linear-gradient(160deg, rgba(12,34,64,.96), rgba(6,16,36,.98)); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 30px 90px rgba(0,0,0,.6), 0 0 40px rgba(84,183,255,.12); overflow:hidden; animation:agent-rise .22s cubic-bezier(.22,1,.36,1) both; }
390
+ .agent-explorer-head { display:flex; align-items:center; justify-content:space-between; padding:.85rem 1rem; border-bottom:1px solid rgba(112,168,228,.16); }
391
+ .agent-explorer-title { color:#8ed6ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.68rem; font-weight:800; letter-spacing:.14em; text-transform:uppercase; text-shadow:0 0 14px rgba(84,183,255,.4); }
392
+ .agent-explorer-note { margin:0; padding:.5rem 1rem 0; color:#5f7b9c; font-size:.6rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; }
393
+ .agent-explorer-bar { display:flex; align-items:center; gap:.5rem; padding:.6rem .75rem; border-bottom:1px solid rgba(112,168,228,.1); }
394
+ .agent-explorer-up { flex:0 0 auto; width:1.9rem; height:1.9rem; padding:0; border:1px solid rgba(112,168,228,.3); border-radius:.45rem; color:#9fd4ff; background:rgba(84,183,255,.1); font-size:.85rem; cursor:pointer; transition:border-color .12s, background .12s, color .12s, opacity .12s; }
395
+ .agent-explorer-up:hover:not(:disabled) { border-color:rgba(84,183,255,.65); color:#fff; background:rgba(84,183,255,.2); }
396
+ .agent-explorer-up:disabled { opacity:.35; cursor:default; }
397
+ .agent-explorer-crumbs { flex:1 1 auto; min-width:0; display:flex; align-items:center; flex-wrap:wrap; gap:.1rem; overflow:hidden; }
398
+ .agent-explorer-crumb { display:inline-flex; align-items:center; white-space:nowrap; color:#45597a; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; }
399
+ .agent-explorer-sep { margin:0 .1rem; opacity:.6; }
400
+ .agent-explorer-crumb-btn { padding:.16rem .34rem; border:1px solid transparent; border-radius:.35rem; color:#9fc3ea; background:transparent; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; cursor:pointer; transition:color .12s, background .12s, border-color .12s; }
401
+ .agent-explorer-crumb-btn:hover { color:#dcecff; background:rgba(84,183,255,.1); }
402
+ .agent-explorer-crumb-btn.on { color:#63bdff; background:rgba(40,137,235,.16); border-color:rgba(84,183,255,.3); }
403
+ .agent-explorer-roots { flex:0 0 auto; display:flex; gap:.3rem; }
404
+ .agent-explorer-root { max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding:.18rem .5rem; border:1px solid rgba(245,158,11,.32); border-radius:.4rem; color:#ffd479; background:rgba(245,158,11,.1); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; cursor:pointer; transition:filter .12s, background .12s; }
405
+ .agent-explorer-root:hover { filter:brightness(1.15); background:rgba(245,158,11,.18); }
406
+ .agent-explorer-body { flex:1 1 auto; min-height:0; overflow-y:auto; padding:.4rem .5rem; }
407
+ .agent-explorer-list { display:flex; flex-direction:column; gap:.15rem; }
408
+ .agent-explorer-row { display:flex; align-items:center; gap:.5rem; padding:.28rem .45rem; border-radius:.5rem; transition:background .12s; }
409
+ .agent-explorer-row:hover { background:rgba(84,183,255,.07); }
410
+ .agent-explorer-name { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; padding:.22rem .4rem; border:1px solid transparent; border-radius:.4rem; color:#cfe3fb; background:transparent; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.72rem; text-align:left; cursor:pointer; transition:color .12s, background .12s, border-color .12s; }
411
+ .agent-explorer-name:hover { color:#fff; background:rgba(40,137,235,.14); border-color:rgba(84,183,255,.25); }
412
+ .agent-explorer-tag { flex:0 0 auto; padding:.1rem .42rem; border-radius:999px; color:#6df0bb; border:1px solid rgba(52,211,153,.32); background:rgba(16,185,129,.12); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.52rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase; }
413
+ .agent-explorer-use { flex:0 0 auto; padding:.24rem .6rem; border:1px solid rgba(84,183,255,.4); border-radius:.4rem; color:#9fd4ff; background:rgba(84,183,255,.12); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:700; cursor:pointer; transition:border-color .12s, background .12s, color .12s, transform .12s; }
414
+ .agent-explorer-use:hover { border-color:rgba(84,183,255,.75); color:#fff; background:rgba(40,137,235,.28); transform:translateY(-1px); }
415
+ .agent-explorer-empty, .agent-explorer-error { margin:0; padding:1rem .6rem; color:#45597a; font-size:.64rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; }
416
+ .agent-explorer-error { color:#fda4af; }
417
+ .agent-explorer-foot { display:flex; align-items:center; gap:.6rem; justify-content:space-between; padding:.7rem .9rem; border-top:1px solid rgba(112,168,228,.14); }
418
+ .agent-explorer-current { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; }
419
+ .agent-explorer-actions { flex:0 0 auto; display:flex; gap:.4rem; }
420
+ .agent-explorer-btn { padding:.4rem .8rem; border:1px solid rgba(84,183,255,.4); border-radius:.5rem; color:#9fd4ff; background:linear-gradient(135deg, rgba(40,137,235,.3), rgba(90,67,183,.22)); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:700; cursor:pointer; transition:filter .14s, transform .14s, opacity .14s; }
421
+ .agent-explorer-btn:hover:not(:disabled) { filter:brightness(1.18); transform:translateY(-1px); }
422
+ .agent-explorer-btn:disabled { opacity:.4; cursor:default; }
423
+ .agent-explorer-btn.ghost { background:transparent; border-color:rgba(112,168,228,.25); color:#8fb8e4; }
424
+ .agent-explorer-browse { width:100%; margin-top:.45rem; padding:.4rem .6rem; border:1px dashed rgba(112,168,228,.3); border-radius:.5rem; color:#8fb8e4; background:rgba(84,183,255,.06); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:700; cursor:pointer; transition:border-color .12s, background .12s, color .12s; }
425
+ .agent-explorer-browse:hover { border-color:rgba(84,183,255,.6); color:#bfe9ff; background:rgba(84,183,255,.12); }
426
+ .agent-workspace-btn { display:grid; place-items:center; padding:.4rem .55rem; border:1px solid rgba(112,168,228,.24); border-radius:.55rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.82rem; line-height:1; cursor:pointer; transition:border-color .14s, color .14s, background .14s, transform .14s; }
427
+ .agent-workspace-btn:hover { border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px); }
428
+ .agent-workspace-btn.on { color:#9fe8ff; border-color:rgba(99,189,255,.6); background:rgba(40,137,235,.2); }
429
+
430
+ .agent-tabs { display:inline-flex; gap:.2rem; padding:.18rem; border:1px solid rgba(112,168,228,.18); border-radius:.6rem; background:rgba(8,25,48,.5); }
431
+ .agent-tab { padding:.32rem .7rem; border:1px solid transparent; border-radius:.45rem; color:#7d94b2; background:transparent; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:700; letter-spacing:.08em; cursor:pointer; transition:color .14s, background .14s, border-color .14s; }
432
+ .agent-tab:hover { color:#dcecff; }
433
+ .agent-tab.on { color:#bfe9ff; background:linear-gradient(135deg, rgba(38,130,225,.4), rgba(90,67,183,.28)); border-color:rgba(84,183,255,.45); box-shadow:0 0 12px rgba(84,183,255,.22); }
434
+
435
+ .agent-output { display:flex; flex-direction:column; gap:.7rem; }
436
+ .agent-output-head { display:flex; align-items:center; gap:.6rem; padding:.5rem 0; border-bottom:1px solid rgba(112,168,228,.12); }
437
+ .agent-output-title { color:#63bdff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.66rem; font-weight:800; letter-spacing:.12em; text-transform:uppercase; }
438
+ .agent-output-count { color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; }
439
+ .agent-output-clear { margin-left:auto; padding:.3rem .6rem; border:1px solid rgba(112,168,228,.28); border-radius:.4rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; cursor:pointer; transition:border-color .12s, background .12s; }
440
+ .agent-output-clear:hover { border-color:rgba(84,183,255,.5); background:rgba(84,183,255,.14); }
441
+ .agent-output-empty { color:#45597a; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; text-align:center; padding:2rem 1rem; }
442
+ .agent-output-list { display:flex; flex-direction:column; gap:.5rem; }
443
+ .agent-output-row { border:1px solid rgba(112,168,228,.2); border-radius:.7rem; background:rgba(8,25,48,.55); overflow:hidden; }
444
+ .agent-output-row.running { border-color:rgba(84,183,255,.4); }
445
+ .agent-output-row.ok { border-color:rgba(52,211,153,.25); }
446
+ .agent-output-row.err { border-color:rgba(251,113,133,.3); }
447
+ .agent-output-row-head { display:flex; align-items:center; gap:.5rem; padding:.5rem .7rem; cursor:pointer; }
448
+ .agent-output-dot { width:.42rem; height:.42rem; border-radius:50%; background:#5f7b9c; }
449
+ .agent-output-dot.running { background:#54b7ff; box-shadow:0 0 8px rgba(84,183,255,.7); animation:agent-pulse 1s ease-in-out infinite; }
450
+ .agent-output-name { color:#9fd4ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; font-weight:700; }
451
+ .agent-output-summary { flex:1; color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
452
+ .agent-output-status { color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; text-transform:uppercase; }
453
+ .agent-output-body { border-top:1px solid rgba(112,168,228,.1); background:rgba(0,0,0,.2); padding:.5rem; }
454
+
455
+ .agent-preview { display:flex; flex-direction:column; gap:.6rem; height:100%; min-height:60vh; }
456
+ .agent-preview-bar { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; padding:.5rem .7rem; border:1px solid rgba(112,168,228,.2); border-radius:.7rem; background:rgba(8,25,48,.6); }
457
+ .agent-preview-label { color:#9fd4ff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; font-weight:700; }
458
+ .agent-preview-badge { padding:.15rem .4rem; border-radius:999px; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; font-weight:700; text-transform:uppercase; }
459
+ .agent-preview-badge.running { color:#7ee8bd; background:rgba(16,185,129,.15); border:1px solid rgba(52,211,153,.3); }
460
+ .agent-preview-badge.building { color:#8ed6ff; background:rgba(84,183,255,.15); border:1px solid rgba(84,183,255,.3); }
461
+ .agent-preview-badge.error { color:#fecdd3; background:rgba(244,63,94,.15); border:1px solid rgba(251,113,133,.3); }
462
+ .agent-preview-badge.stopped, .agent-preview-badge.not_found { color:#ffd479; background:rgba(245,158,11,.12); border:1px solid rgba(255,196,107,.3); }
463
+ .agent-preview-actions { display:inline-flex; gap:.35rem; margin-left:auto; }
464
+ .agent-preview-btn, .agent-preview-link { padding:.3rem .6rem; border:1px solid rgba(84,183,255,.35); border-radius:.45rem; color:#9fd4ff; background:rgba(84,183,255,.1); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.58rem; font-weight:700; cursor:pointer; text-decoration:none; transition:filter .12s, background .12s; }
465
+ .agent-preview-btn:hover, .agent-preview-link:hover { filter:brightness(1.15); background:rgba(84,183,255,.18); }
466
+ .agent-preview-btn:disabled { opacity:.4; cursor:not-allowed; }
467
+ .agent-preview-btn.ghost { border-color:rgba(112,168,228,.25); color:#8fb8e4; background:transparent; }
468
+ .agent-preview-error { padding:.5rem .7rem; border:1px solid rgba(251,113,133,.3); border-radius:.5rem; color:#fecdd3; background:rgba(244,63,94,.1); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.62rem; white-space:pre-wrap; }
469
+ .agent-preview-empty { color:#6e8aaa; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; line-height:1.7; text-align:center; padding:2rem 1rem; border:1px dashed rgba(112,168,228,.2); border-radius:.7rem; background:rgba(8,25,48,.4); }
470
+ .agent-preview-empty b { color:#9fd4ff; }
471
+ .agent-preview-empty code { background:rgba(84,183,255,.12); padding:.1em .3em; border-radius:.3em; }
472
+ .agent-preview-hint { color:#4d6378; font-size:.6rem; margin-top:.6rem; }
473
+ .agent-preview-frame { flex:1 1 auto; width:100%; min-height:420px; border:1px solid rgba(112,168,228,.2); border-radius:.7rem; background:#fff; }
474
+ .agent-preview-note { color:#4d6378; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; text-align:center; }
475
+ .agent-preview-note a { color:#6fcaff; }
476
+
477
+ .agent-side-actions { display:inline-flex; gap:.3rem; margin-left:auto; }
478
+ .agent-side-action { padding:.18rem .5rem; border:1px solid rgba(112,168,228,.28); border-radius:.4rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.54rem; cursor:pointer; transition:border-color .12s, background .12s, color .12s; }
479
+ .agent-side-action:hover { border-color:rgba(84,183,255,.6); color:#bfe9ff; background:rgba(84,183,255,.16); }
480
+
481
+ .agent-markdown { white-space:normal; overflow-wrap:break-word; font-family:system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; font-size:.85rem; line-height:1.72; color:#dcecff; }
482
+ .agent-markdown > :first-child { margin-top:0; }
483
+ .agent-markdown > :last-child { margin-bottom:0; }
484
+ .agent-markdown h1, .agent-markdown h2, .agent-markdown h3, .agent-markdown h4 { margin:1.1em 0 .5em; color:#eef8ff; line-height:1.3; letter-spacing:-.02em; }
485
+ .agent-markdown h1 { font-size:1.28rem; } .agent-markdown h2 { font-size:1.12rem; } .agent-markdown h3 { font-size:1rem; } .agent-markdown h4 { font-size:.92rem; }
486
+ .agent-markdown p { margin:.5em 0; }
487
+ .agent-markdown a { color:#6fcaff; text-decoration:none; border-bottom:1px solid rgba(111,202,255,.35); }
488
+ .agent-markdown a:hover { color:#bfe9ff; }
489
+ .agent-markdown ul, .agent-markdown ol { margin:.5em 0; padding-left:1.4em; }
490
+ .agent-markdown li { margin:.2em 0; }
491
+ .agent-markdown strong { color:#fff; font-weight:700; }
492
+ .agent-markdown em { color:#cfdfff; }
493
+ .agent-markdown blockquote { margin:.6em 0; padding:.25em .9em; border-left:3px solid #54b7ff; color:#8fa5c0; background:rgba(84,183,255,.06); border-radius:0 .5rem .5rem 0; }
494
+ .agent-markdown code { font-family:"Cascadia Code", ui-monospace, monospace; font-size:.8em; padding:.15em .35em; border-radius:.3em; background:rgba(84,183,255,.12); color:#9fd4ff; }
495
+ .agent-markdown pre { margin:.7em 0; padding:.8em .9em; overflow:auto; border:1px solid rgba(112,168,228,.18); border-radius:.6rem; background:rgba(0,0,0,.35); }
496
+ .agent-markdown pre code { padding:0; background:none; font-size:.78rem; color:#e8f2ff; }
497
+ .agent-markdown pre code.hljs { display:block; background:transparent; }
498
+ .agent-markdown table { width:100%; margin:.7em 0; border-collapse:collapse; font-size:.8rem; }
499
+ .agent-markdown th, .agent-markdown td { padding:.4em .6em; border:1px solid rgba(112,168,228,.18); text-align:left; }
500
+ .agent-markdown th { background:rgba(84,183,255,.08); color:#bfe9ff; }
501
+ .agent-markdown hr { margin:1em 0; border:0; height:1px; background:rgba(112,168,228,.15); }
502
+ .agent-markdown .hljs { display:block; background:transparent; color:#e8f2ff; }
503
+ .agent-markdown .hljs-keyword, .agent-markdown .hljs-selector-tag, .agent-markdown .hljs-meta .hljs-keyword { color:#c792ea; }
504
+ .agent-markdown .hljs-string, .agent-markdown .hljs-attr, .agent-markdown .hljs-regexp, .agent-markdown .hljs-template-string { color:#7fe0a8; }
505
+ .agent-markdown .hljs-number, .agent-markdown .hljs-literal, .agent-markdown .hljs-symbol, .agent-markdown .hljs-bullet, .agent-markdown .hljs-params { color:#e2a76f; }
506
+ .agent-markdown .hljs-comment, .agent-markdown .hljs-quote { color:#6b85a3; font-style:italic; }
507
+ .agent-markdown .hljs-title, .agent-markdown .hljs-title.function_, .agent-markdown .hljs-function .hljs-title, .agent-markdown .hljs-section { color:#82c6ff; }
508
+ .agent-markdown .hljs-built_in, .agent-markdown .hljs-selector-class, .agent-markdown .hljs-selector-id { color:#e5c07b; }
509
+ .agent-markdown .hljs-tag, .agent-markdown .hljs-name, .agent-markdown .hljs-variable, .agent-markdown .hljs-template-variable { color:#f18ac6; }
510
+ .agent-markdown .hljs-meta, .agent-markdown .hljs-doctag { color:#56b6c2; }
511
+
512
+ /* ── Two-pane split diff (edit old_string / new_string) ───────────────── */
513
+ .agent-split { display:flex; flex-direction:column; border-top:1px solid rgba(112,168,228,.12); background:rgba(0,0,0,.28); overflow:hidden; }
514
+ .agent-split-head { display:flex; align-items:center; gap:.5rem; padding:.45rem .7rem; border-bottom:1px solid rgba(112,168,228,.12); background:linear-gradient(180deg, rgba(16,38,70,.5), rgba(8,20,42,.55)); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.56rem; letter-spacing:.06em; text-transform:uppercase; }
515
+ .agent-split-head-left { flex:1; color:#fda4af; }
516
+ .agent-split-head-right { flex:1; color:#6df0bb; text-align:right; }
517
+ .split-badge { display:inline-block; padding:.05rem .35rem; border-radius:.3rem; font-size:.52rem; font-weight:800; letter-spacing:.08em; vertical-align:middle; margin:0 .25rem; }
518
+ .split-badge.del { color:#fecdd3; background:rgba(244,63,94,.22); border:1px solid rgba(244,63,94,.35); }
519
+ .split-badge.add { color:#a7f3d0; background:rgba(16,185,129,.22); border:1px solid rgba(16,185,129,.35); }
520
+ .agent-split-grid { display:grid; grid-template-columns:1fr 1fr; min-height:0; max-height:420px; }
521
+ .agent-split-pane { position:relative; min-width:0; overflow:auto; background:rgba(0,0,0,.18); scrollbar-width:thin; scrollbar-color:rgba(112,168,228,.3) transparent; }
522
+ .agent-split-pane.pane-left { border-right:1px solid rgba(112,168,228,.12); }
523
+ .agent-split-pane .agent-copy { position:sticky; top:.4rem; float:right; margin:.4rem .4rem 0 0; z-index:3; }
524
+ .split-pane-label { position:sticky; top:0; z-index:2; padding:.28rem .6rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.52rem; letter-spacing:.1em; text-transform:uppercase; backdrop-filter:blur(8px); }
525
+ .pane-left .split-pane-label { color:#fda4af; background:rgba(244,63,94,.1); border-bottom:1px solid rgba(244,63,94,.18); }
526
+ .pane-right .split-pane-label { color:#6df0bb; background:rgba(16,185,129,.1); border-bottom:1px solid rgba(16,185,129,.18); }
527
+ .split-lines { padding:.5rem 0 .6rem; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.7rem; line-height:1.65; }
528
+ .split-line { display:flex; gap:.35rem; padding:.02rem .5rem; white-space:pre-wrap; word-break:break-word; }
529
+ .split-line.del { color:#fecdd3; background:rgba(244,63,94,.14); }
530
+ .split-line.add { color:#a7f3d0; background:rgba(16,185,129,.14); }
531
+ .split-line.context { color:#cfe3fb; }
532
+ .split-line.hunk { color:#67c8ff; background:rgba(84,183,255,.08); font-size:.62rem; letter-spacing:.02em; border-top:1px dashed rgba(84,183,255,.22); border-bottom:1px dashed rgba(84,183,255,.22); margin:.25rem 0; padding:.2rem .5rem; }
533
+ .split-line.empty { background:rgba(112,168,228,.04); min-height:1.15em; }
534
+ .split-num { flex:0 0 2.2rem; text-align:right; color:#3e5573; font-size:.62rem; user-select:none; }
535
+ .split-marker { flex:0 0 1ch; text-align:center; opacity:.7; }
536
+ .split-line.del .split-marker { color:#fb7185; }
537
+ .split-line.add .split-marker { color:#34d399; }
538
+ .split-text { flex:1 1 auto; min-width:0; }
539
+ .split-text .hljs { background:transparent; }
540
+ .split-hunk { opacity:.9; }
541
+ @media (max-width:760px) { .agent-split-grid { grid-template-columns:1fr; max-height:none; } .agent-split-pane.pane-left { border-right:0; border-bottom:1px solid rgba(112,168,228,.12); } .agent-split-pane { max-height:260px; } }
542
+
543
+ @media (max-width:1100px) {
544
+ .agent-side { flex-basis:320px; }
545
+ .agent-bubble { max-width:100%; }
546
+ }
547
+
548
+ @media (max-width:1024px) {
549
+ .agent-side { position:fixed; top:0; right:0; bottom:0; z-index:50; width:min(340px, 88vw); border-left:1px solid rgba(112,168,228,.2); background:linear-gradient(180deg, rgba(10,28,55,.98), rgba(6,15,34,.98)); box-shadow:-18px 0 60px rgba(0,0,0,.5); backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); transform:translateX(100%); transition:transform .24s cubic-bezier(.22,1,.36,1); }
550
+ .agent-side.open { transform:translateX(0); }
551
+ .agent-side-toggle { display:grid; }
552
+ .agent-main { padding-right:1.4rem; }
553
+
554
+ .agent-shell.agent-side-on > .agent-workspace { z-index:60; }
555
+ .agent-shell.agent-side-on > .agent-workspace .agent-side { top:3.8rem; height:calc(100dvh - 3.8rem); }
556
+ .agent-shell.agent-side-on > .agent-header { z-index:63; }
557
+ }
558
+
559
+ @media (max-width:860px) {
560
+ .agent-header-inner, .agent-workspace, .agent-error, .agent-notice, .agent-footer-inner, .agent-footer-bar { width:100%; max-width:none; }
561
+ .agent-header-inner { padding:.6rem .9rem; }
562
+ .agent-workspace { padding:0; }
563
+ .agent-main { padding:1.2rem .9rem; }
564
+ .agent-brand-sub, .agent-status { display:none; }
565
+ .agent-select { max-width:150px; }
566
+ .agent-select-label { display:none; }
567
+ .agent-footer-inner, .agent-footer-bar { padding:.75rem .9rem; }
568
+ .agent-footer-bar { padding-bottom:.2rem; }
569
+ .agent-footer-hint { display:none; }
570
+ .agent-modes { flex-wrap:wrap; }
571
+ .agent-mode { padding:.4rem .55rem; font-size:.58rem; }
572
+ }
573
+
574
+ @media (max-width:640px) {
575
+ .agent-main { padding:1rem .75rem; }
576
+ .agent-msg { max-width:100%; }
577
+ .agent-bubble { font-size:.82rem; padding:.65rem .8rem; }
578
+ .agent-row-user .agent-bubble, .agent-row-agent .agent-bubble { max-width:100%; }
579
+ .agent-tool-head { padding:.45rem .65rem; font-size:.66rem; }
580
+ .agent-badge-head { padding:.4rem .65rem; font-size:.66rem; }
581
+ .agent-list { gap:.8rem; }
582
+ .agent-hero { margin:2vh auto 0; padding:1.5rem 1rem; }
583
+ .agent-hero h2 { font-size:1.25rem; }
584
+ .agent-footer-bar { gap:.45rem; }
585
+ .agent-footer-inner { gap:.5rem; }
586
+ .agent-input { font-size:.8rem; }
587
+ .agent-send { padding:.65rem .9rem; font-size:.76rem; }
588
+ .agent-team-toggle, .agent-enhance-toggle { padding:.36rem .55rem; font-size:.56rem; }
589
+ .agent-workspace-btn { padding:.36rem .5rem; font-size:.72rem; }
590
+ .agent-enhance, .agent-suggest { bottom:3.9rem; width:calc(100vw - 16px); }
591
+ .agent-toast { right:.9rem; top:3.6rem; }
592
+ .agent-scroll-down { right:1rem; bottom:6rem; }
593
+ }
594
+
595
+ @media (max-width:480px) {
596
+ .agent-header-inner { padding:.5rem .7rem; }
597
+ .agent-brand-badge { width:1.9rem; height:1.9rem; }
598
+ .agent-brand-name { font-size:.68rem; }
599
+ .agent-brand-text { display:none; }
600
+ .agent-clear { padding:.45rem .6rem; font-size:.62rem; }
601
+ .agent-settings-btn, .agent-side-toggle { width:1.9rem; height:1.9rem; font-size:.8rem; }
602
+ .agent-settings { width:100vw; }
603
+ .agent-side { width:88vw; }
604
+ .agent-modes { width:100%; }
605
+ .agent-mode { flex:1; text-align:center; }
606
+ .agent-team-toggle, .agent-enhance-toggle { flex:1; justify-content:center; }
607
+ .agent-workspace-btn { flex:1; justify-content:center; }
608
+ .agent-footer-bar { display:flex; }
609
+ .agent-footer-inner { flex-wrap:nowrap; }
610
+ .agent-hero-chips { flex-direction:column; width:100%; }
611
+ .agent-hero-chip { width:100%; text-align:center; }
612
+ }`;
613
+
614
+ export interface ExplorerOptions {
615
+ endpoint?: string;
616
+ title?: string;
617
+ appName?: string;
618
+ sdl?: string;
619
+ introspection?: boolean;
620
+ }
621
+
622
+ export function getExplorerHtml(opts: ExplorerOptions = {}): string {
623
+ const endpoint = opts.endpoint ?? '/graphql';
624
+ const title = opts.title ?? 'BhooAI Nexus — GraphQL Sandbox';
625
+ const appName = opts.appName ?? 'backend';
626
+ const introspection = opts.introspection !== false;
627
+ const sdlPreview = opts.sdl ? opts.sdl.slice(0, 4000).replace(/`/g, '\\`').replace(/\$/g, '\\$') : '';
628
+ const rawSdl = opts.sdl ?? '';
629
+ const operations = extractOperations(rawSdl);
630
+ const defaultQuery = buildDefaultQuery(operations);
631
+
632
+ // CDN versions pinned to avoid breaking changes
633
+ const CDN = {
634
+ graphiqlCss: 'https://unpkg.com/graphiql@3.6.0/graphiql.min.css',
635
+ graphiqlJs: 'https://unpkg.com/graphiql@3.6.0/graphiql.min.js',
636
+ hljsCss: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css',
637
+ hljsJs: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js',
638
+ react: 'https://unpkg.com/react@18/umd/react.production.min.js',
639
+ reactDom: 'https://unpkg.com/react-dom@18/umd/react-dom.production.min.js',
640
+ graphqlWs: 'https://unpkg.com/graphql-ws@5/umd/graphql-ws.min.js',
641
+ };
642
+
643
+ return `<!doctype html>
644
+ <html lang="en">
645
+ <head>
646
+ <meta charset="utf-8" />
647
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
648
+ <title>${escapeHtml(title)}</title>
649
+ <link rel="stylesheet" href="${CDN.graphiqlCss}" />
650
+ <link rel="stylesheet" href="${CDN.hljsCss}" />
651
+ <style>
652
+ :root{ color-scheme: dark light; }
653
+ *{box-sizing:border-box}
654
+ html,body{height:100%;margin:0;padding:0}
655
+ /* ── AURORA GLASS THEME — copied verbatim from bhooai-agent AGENT_CSS ── */
656
+ ${AURORA_CSS}
657
+ /* ── full-width overrides (explorer is edge-to-edge, not centered 1400px) ── */
658
+ .agent-header-inner, .agent-workspace { width:100%; max-width:none; margin:0; padding-inline:1rem; }
659
+ .agent-header::after { left:1rem; right:1rem; }
660
+ .agent-main { padding:.8rem 1rem 1rem; }
661
+ .agent-side { flex:0 0 380px; padding:1rem .8rem 1rem 1rem; }
662
+ @media (max-width: 960px){ .agent-side { display:none; } }
663
+ /* ── GraphiQL aurora theming (override its light-mode CDN defaults) ──
664
+ GraphiQL's CDN only applies its dark palette via body.graphiql-dark or
665
+ prefers-color-scheme — without that class everything renders light.
666
+ Our overrides target BOTH body classes AND the raw selectors so they
667
+ win the cascade over the CDN defaults for .graphiql-main, sidebar,
668
+ editors, toolbar, sessions, footer and doc-explorer. */
669
+ body.graphiql-dark .graphiql-container, body.graphiql-light .graphiql-container,
670
+ .graphiql-container,
671
+ .graphiql-container .CodeMirror-info,
672
+ .graphiql-container .CodeMirror-lint-tooltip,
673
+ .graphiql-dialog,
674
+ .graphiql-dialog-overlay,
675
+ .graphiql-tooltip {
676
+ /* GraphiQL HSL tokens → aurora palette */
677
+ --color-primary: 205, 100%, 66%; /* #54b7ff blue */
678
+ --color-secondary: 258, 100%, 77%; /* #9f7bff violet */
679
+ --color-tertiary: 188, 100%, 52%;
680
+ --color-info: 208, 100%, 56%;
681
+ --color-success: 158, 70%, 55%; /* #5ef0b3 mint */
682
+ --color-warning: 41, 100%, 63%;
683
+ --color-error: 352, 96%, 68%; /* #fb7185 rose */
684
+ --color-neutral: 215, 45%, 74%;
685
+ --color-base: 220, 60%, 6%; /* dark base: #040b18 */
686
+ --alpha-secondary: .85;
687
+ --alpha-tertiary: .55;
688
+ --alpha-background-heavy: .16;
689
+ --alpha-background-medium: .09;
690
+ --alpha-background-light: .05;
691
+ --font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
692
+ --font-family-mono: ui-monospace, SFMono-Regular, Consolas, monospace;
693
+ --editor-background: rgba(8, 25, 48, .55);
694
+ --popover-box-shadow: 0 24px 70px rgba(0,0,0,.5), 0 0 30px rgba(84,183,255,.14);
695
+ --popover-border: 1px solid rgba(112,168,228,.24);
696
+ --session-header-height: 44px;
697
+ --sidebar-width: 48px;
698
+ --toolbar-width: 36px;
699
+ }
700
+ /* main editor surface: glass, translucent so aurora shows through */
701
+ .graphiql-container { background: transparent; height:100%; }
702
+ .graphiql-container .graphiql-main, .graphiql-container .graphiql-sessions { background: transparent; }
703
+ /* .graphiql-editors — the big white card holding query editor + result pane */
704
+ .graphiql-container .graphiql-editors {
705
+ background: linear-gradient(160deg, rgba(16,38,70,.55), rgba(8,20,42,.62));
706
+ border: 1px solid rgba(112,168,228,.18);
707
+ box-shadow: inset 0 1px rgba(255,255,255,.05), 0 18px 50px rgba(0,0,0,.28);
708
+ backdrop-filter: blur(18px) saturate(135%); -webkit-backdrop-filter: blur(18px) saturate(135%);
709
+ border-radius: .9rem;
710
+ }
711
+ /* query editor section (toolbar + codemirror) */
712
+ .graphiql-container .graphiql-query-editor { border-bottom-color: rgba(112,168,228,.14); background: transparent; }
713
+ /* left sidebar (icon rail: docs/history/plugins) */
714
+ .graphiql-container .graphiql-sidebar { background: linear-gradient(180deg, rgba(9,24,48,.6), rgba(4,14,29,.4)); border-right: 1px solid rgba(112,168,228,.14); }
715
+ .graphiql-container .graphiql-sidebar button { color: #6e8aaa; border-radius: .5rem; transition: color .14s, background .14s; }
716
+ .graphiql-container .graphiql-sidebar button:hover { color: #dcecff; background: rgba(84,183,255,.12); }
717
+ .graphiql-container .graphiql-sidebar button.active { color: #8ed6ff; background: rgba(84,183,255,.16); box-shadow: 0 0 12px rgba(84,183,255,.25); }
718
+ .graphiql-container .graphiql-sidebar button > svg { fill: currentColor; }
719
+ /* vertical toolbar next to query editor */
720
+ .graphiql-container .graphiql-toolbar { background: linear-gradient(180deg, rgba(9,24,48,.5), rgba(4,14,29,.3)); border-right: 1px solid rgba(112,168,228,.12); }
721
+ .graphiql-container .graphiql-toolbar button { color: #6e8aaa; border-radius: .45rem; }
722
+ .graphiql-container .graphiql-toolbar button:hover { color: #dcecff; background: rgba(84,183,255,.12); }
723
+ .graphiql-container .graphiql-toolbar button.active { color: #8ed6ff; background: rgba(84,183,255,.16); }
724
+ .graphiql-container .graphiql-toolbar-icon { color: currentColor; }
725
+ /* editor-tools row (Variables / Headers / Prettify toggles + drag handle) */
726
+ .graphiql-container .graphiql-editor-tools { background: rgba(4,14,29,.35); border-top: 1px solid rgba(112,168,228,.1); }
727
+ .graphiql-container .graphiql-editor-tools button { color: #6e8aaa; }
728
+ .graphiql-container .graphiql-editor-tools button:hover { color: #dcecff; background: rgba(84,183,255,.1); border-radius:.4rem; }
729
+ .graphiql-container .graphiql-editor-tools button.active { color: #8ed6ff; border-bottom-color: #54b7ff; }
730
+ /* session header (tabs + execute) */
731
+ .graphiql-container .graphiql-session-header { border-bottom:1px solid rgba(112,168,228,.14); background:linear-gradient(180deg, rgba(9,24,48,.55), rgba(4,14,29,.35)); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px); }
732
+ .graphiql-container .graphiql-session-header-right button, .graphiql-container .graphiql-toolbar button { color:#9fb3d0; }
733
+ .graphiql-container .graphiql-session-header-right button:hover, .graphiql-container .graphiql-toolbar button:hover { color:#dcecff; background:rgba(84,183,255,.14); }
734
+ /* response footer */
735
+ .graphiql-container .graphiql-footer { border-top-color: rgba(112,168,228,.14); background: rgba(4,14,29,.35); }
736
+ /* doc explorer panel (opens from sidebar) */
737
+ .graphiql-container .graphiql-doc-explorer { background: linear-gradient(160deg, rgba(16,38,70,.6), rgba(8,20,42,.66)); backdrop-filter: blur(18px) saturate(135%); -webkit-backdrop-filter: blur(18px) saturate(135%); border-left: 1px solid rgba(112,168,228,.14); }
738
+ .graphiql-container .graphiql-doc-explorer-header, .graphiql-container .graphiql-doc-explorer-section-title { color: #9fd4ff; border-color: rgba(112,168,228,.14); background: transparent; }
739
+ .graphiql-container .graphiql-doc-explorer-section-content, .graphiql-container .graphiql-doc-explorer-item { color: #cfe3fb; background: transparent; }
740
+ .graphiql-container .graphiql-doc-explorer-item:hover { background: rgba(84,183,255,.1); }
741
+ .graphiql-container .graphiql-doc-explorer-field-name { color: #8ed6ff; }
742
+ .graphiql-container .graphiql-doc-explorer-type-name { color: #c4b5fd; }
743
+ .graphiql-container .graphiql-doc-explorer-argument-name { color: #7fe0a8; }
744
+ .graphiql-container .graphiql-doc-explorer-search-input, .graphiql-container .graphiql-doc-explorer-search input[type=text], .graphiql-container .graphiql-doc-explorer-search input { background: rgba(255,255,255,.06); border:1px solid rgba(112,168,228,.22); color:#e8f2ff; }
745
+ .graphiql-container .graphiql-doc-explorer-back { color: #9fd4ff; }
746
+ /* history panel inside graphiql */
747
+ .graphiql-container .graphiql-history-item { color:#cfe3fb; border-color: rgba(112,168,228,.14); background: rgba(8,25,48,.55); }
748
+ .graphiql-container .graphiql-history-item:hover { background: rgba(84,183,255,.1); }
749
+ .graphiql-container .graphiql-history-header { color: #5f7b9c; border-color: rgba(112,168,228,.14); }
750
+ /* History button in GraphiQL sidebar */
751
+ .graphiql-container .graphiql-history-btn {
752
+ position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
753
+ background: none; border: none; color: #6e8aaa; cursor: pointer; padding: 4px 8px;
754
+ border-radius: .4rem; font-size: .7rem; transition: color .14s, background .14s;
755
+ }
756
+ .graphiql-container .graphiql-history-btn:hover { color: #dcecff; background: rgba(84,183,255,.12); }
757
+ .graphiql-container .graphiql-history-btn.active { color: #8ed6ff; background: rgba(84,183,255,.16); box-shadow: 0 0 8px rgba(84,183,255,.25); }
758
+ /* dropdown menus + buttons */
759
+ .graphiql-container .graphiql-dropdown-content { background: linear-gradient(160deg, rgba(12,34,64,.97), rgba(6,16,36,.98)); border:1px solid rgba(99,189,255,.3); box-shadow: 0 16px 40px rgba(0,0,0,.5); }
760
+ .graphiql-container .graphiql-dropdown-item { color: #cfe3fb; }
761
+ .graphiql-container .graphiql-dropdown-item:hover { background: rgba(84,183,255,.15); color:#fff; }
762
+ .graphiql-container .graphiql-button { border-color: rgba(112,168,228,.24); color: #9fd4ff; background: rgba(84,183,255,.1); }
763
+ .graphiql-container .graphiql-button:hover { border-color: rgba(84,183,255,.55); color:#fff; background: rgba(84,183,255,.18); }
764
+ /* result pane */
765
+ .graphiql-container .graphiql-response-result, .graphiql-container pre.result-window, .graphiql-container .result-window {
766
+ font-family:var(--font-family-mono, ui-monospace, monospace);
767
+ }
768
+ .graphiql-container .result-window, .graphiql-container .result-window * { background: transparent; }
769
+ /* docs sidebar inside GraphiQL */
770
+ .graphiql-container .graphiql-docs { background:linear-gradient(160deg, rgba(16,38,70,.6), rgba(8,20,42,.66)); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%); color:#dcecff; }
771
+ .graphiql-container .graphiql-docs .graphiql-docs-description, .graphiql-container .graphiql-docs-section, .graphiql-container .graphiql-docs-category-item { color:#cfe3fb; background:transparent; border-color:rgba(112,168,228,.14); }
772
+ .graphiql-container .graphiql-docs .graphiql-docs-category-item:hover { background:rgba(84,183,255,.1); }
773
+ .graphiql-container .graphiql-docs .graphiql-docs-type { color:#8ed6ff; }
774
+ .graphiql-container .graphiql-docs .doc-category-item .field-name { color:#8ed6ff; }
775
+ .graphiql-container .graphiql-docs .doc-category-item .type-name { color:#c4b5fd; }
776
+ /* editor panes + CodeMirror */
777
+ .graphiql-container .CodeMirror, .graphiql-container .result-window, .graphiql-container .graphiql-editor, .graphiql-container pre.CodeMirror-code { background: rgba(8,25,48,.35); }
778
+ .graphiql-container .cm-s-graphiql { background: transparent !important; color:#cfe3fb; }
779
+ .graphiql-container .cm-s-graphiql .CodeMirror-gutters { background:rgba(4,14,29,.4); border-right:1px solid rgba(112,168,228,.12); }
780
+ .graphiql-container .cm-s-graphiql .CodeMirror-linenumber { color:#45597a; }
781
+ .graphiql-container .cm-s-graphiql .CodeMirror-cursor { border-left:2px solid #54b7ff; }
782
+ .graphiql-container .cm-s-graphiql .CodeMirror-selected, .graphiql-container .cm-s-graphiql .CodeMirror-focused .CodeMirror-selected { background:rgba(84,183,255,.22); }
783
+ .graphiql-container .cm-s-graphiql .CodeMirror-matchingbracket { color:#5ef0b3 !important; border-bottom:1px solid #5ef0b3; }
784
+ .graphiql-container .cm-s-graphiql .CodeMirror-activeline-background { background:rgba(84,183,255,.07); }
785
+ /* CodeMirror syntax colors → aurora (match hljs theme) */
786
+ .graphiql-container .cm-s-graphiql .cm-keyword { color:#c792ea; }
787
+ .graphiql-container .cm-s-graphiql .cm-def { color:#82c6ff; }
788
+ .graphiql-container .cm-s-graphiql .cm-property, .graphiql-container .cm-s-graphiql .cm-variable { color:#e8f2ff; }
789
+ .graphiql-container .cm-s-graphiql .cm-variable-2 { color:#7fe0a8; }
790
+ .graphiql-container .cm-s-graphiql .cm-punctuation, .graphiql-container .cm-s-graphiql .cm-operator { color:#8fa8c8; }
791
+ .graphiql-container .cm-s-graphiql .cm-string, .graphiql-container .cm-s-graphiql .cm-string-2 { color:#7fe0a8; }
792
+ .graphiql-container .cm-s-graphiql .cm-number, .graphiql-container .cm-s-graphiql .cm-atom { color:#e2a76f; }
793
+ .graphiql-container .cm-s-graphiql .cm-comment { color:#6b85a3; font-style:italic; }
794
+ .graphiql-container .cm-s-graphiql .cm-attribute { color:#e5c07b; }
795
+ .graphiql-container .cm-s-graphiql .cm-qualifier, .graphiql-container .cm-s-graphiql .cm-builtin { color:#56b6c2; }
796
+ /* toolbar / tabs / buttons */
797
+ .graphiql-container .graphiql-tab, .graphiql-container .graphiql-tab-button, .graphiql-container .graphiql-tab.active { border-color:rgba(112,168,228,.18); background:rgba(8,25,48,.5); color:#9fb3d0; }
798
+ .graphiql-container .graphiql-tab.active { color:#8ed6ff; border-bottom-color:rgba(84,183,255,.6); background:rgba(84,183,255,.1); }
799
+ .graphiql-container .graphiql-tab:hover { color:#dcecff; }
800
+ .graphiql-container button.graphiql-toolbar-button, .graphiql-container .graphiql-toolbar { background:transparent; }
801
+ .graphiql-container .graphiql-execute-button { background:linear-gradient(135deg, rgba(38,130,225,.75), rgba(90,67,183,.65)) !important; border:1px solid rgba(84,183,255,.55); box-shadow:0 0 14px rgba(84,183,255,.35); }
802
+ .graphiql-container .graphiql-execute-button:hover { filter:brightness(1.15); }
803
+ /* dialogs + tooltips + autocomplete */
804
+ .graphiql-dialog, .graphiql-dialog-overlay { background:rgba(2,8,20,.62); }
805
+ .graphiql-dialog-container { background:linear-gradient(160deg, rgba(12,34,64,.96), rgba(6,16,36,.98)) !important; border:1px solid rgba(99,189,255,.28); box-shadow:0 30px 90px rgba(0,0,0,.6); backdrop-filter:blur(22px); }
806
+ .graphiql-container .CodeMirror-hints { background:linear-gradient(160deg, rgba(12,34,64,.97), rgba(6,16,36,.98)); border:1px solid rgba(99,189,255,.3); box-shadow:0 16px 40px rgba(0,0,0,.5); }
807
+ .graphiql-container .CodeMirror-hint { color:#cfe3fb; }
808
+ .graphiql-container .CodeMirror-hint-active { background:rgba(84,183,255,.22); color:#fff; }
809
+ .graphiql-tooltip { background:linear-gradient(160deg, rgba(12,34,64,.96), rgba(6,16,36,.98)); color:#dcecff; border:1px solid rgba(99,189,255,.3); }
810
+ /* GraphiQL variables/editor inputs */
811
+ .graphiql-container input[type=text], .graphiql-container input[type=search], .graphiql-container textarea { background:rgba(255,255,255,.05); border-color:rgba(112,168,228,.24); color:#e8f2ff; }
812
+ .graphiql-container input:focus, .graphiql-container textarea:focus { border-color:rgba(84,183,255,.6); box-shadow:0 0 0 3px rgba(84,183,255,.14); }
813
+ /* light theme: GraphiQL back to light vars + light glass surfaces */
814
+ [data-theme="light"] .graphiql-container,
815
+ [data-theme="light"] .graphiql-container .CodeMirror-info,
816
+ [data-theme="light"] .graphiql-dialog,
817
+ [data-theme="light"] .graphiql-tooltip {
818
+ --color-base: 216, 40%, 98%;
819
+ --color-neutral: 219, 28%, 32%;
820
+ --alpha-background-light: .6;
821
+ --editor-background: rgba(255,255,255,.8);
822
+ --popover-box-shadow: 0 14px 40px rgba(20,50,100,.14);
823
+ --popover-border: 1px solid rgba(60,110,180,.18);
824
+ }
825
+ [data-theme="light"] .graphiql-container .graphiql-editors { background: rgba(255,255,255,.72); border-color: rgba(60,110,180,.16); box-shadow: inset 0 1px rgba(255,255,255,.6), 0 18px 50px rgba(20,50,100,.1); }
826
+ [data-theme="light"] .graphiql-container .graphiql-sidebar { background: rgba(255,255,255,.6); border-right-color: rgba(60,110,180,.14); }
827
+ [data-theme="light"] .graphiql-container .graphiql-toolbar { background: rgba(255,255,255,.55); border-right-color: rgba(60,110,180,.12); }
828
+ [data-theme="light"] .graphiql-container .graphiql-sidebar button, [data-theme="light"] .graphiql-container .graphiql-toolbar button, [data-theme="light"] .graphiql-container .graphiql-editor-tools button { color:#5a6b84; }
829
+ [data-theme="light"] .graphiql-container .graphiql-sidebar button.active, [data-theme="light"] .graphiql-container .graphiql-toolbar button.active { color:#1d6fd0; background:rgba(29,111,208,.14); }
830
+ [data-theme="light"] .graphiql-container .graphiql-editor-tools { background: rgba(240,246,255,.7); border-top-color: rgba(60,110,180,.12); }
831
+ [data-theme="light"] .graphiql-container .graphiql-session-header { background:linear-gradient(180deg, rgba(255,255,255,.85), rgba(235,242,252,.7)); border-bottom-color:rgba(60,110,180,.14); }
832
+ [data-theme="light"] .graphiql-container .graphiql-doc-explorer, [data-theme="light"] .graphiql-container .graphiql-docs { background:rgba(255,255,255,.82); color:#16233c; border-left-color:rgba(60,110,180,.14); }
833
+ [data-theme="light"] .graphiql-container .graphiql-doc-explorer-header, [data-theme="light"] .graphiql-container .graphiql-doc-explorer-section-title { color:#1d6fd0; }
834
+ [data-theme="light"] .graphiql-container .graphiql-doc-explorer-section-content, [data-theme="light"] .graphiql-container .graphiql-doc-explorer-item { color:#3a4c68; }
835
+ [data-theme="light"] .graphiql-container .graphiql-history-item { color:#3a4c68; background:rgba(255,255,255,.7); border-color:rgba(60,110,180,.12); }
836
+ [data-theme="light"] .graphiql-container .graphiql-footer { background: rgba(240,246,255,.7); border-top-color:rgba(60,110,180,.12); }
837
+ [data-theme="light"] .graphiql-container .graphiql-docs .graphiql-docs-section, [data-theme="light"] .graphiql-container .graphiql-docs .graphiql-docs-description { color:#3a4c68; }
838
+ [data-theme="light"] .graphiql-container .CodeMirror, [data-theme="light"] .graphiql-container pre.CodeMirror-code { background:rgba(255,255,255,.85); }
839
+ [data-theme="light"] .graphiql-container .cm-s-graphiql { color:#1c2a44; }
840
+ [data-theme="light"] .graphiql-container .cm-s-graphiql .CodeMirror-gutters { background:rgba(230,240,252,.9); border-right-color:rgba(60,110,180,.12); }
841
+ [data-theme="light"] .graphiql-container .cm-s-graphiql .CodeMirror-linenumber { color:#7a8db0; }
842
+ [data-theme="light"] .graphiql-container .graphiql-tab, [data-theme="light"] .graphiql-container .graphiql-tab.active { background:rgba(255,255,255,.8); color:#3a4c68; border-color:rgba(60,110,180,.14); }
843
+ [data-theme="light"] .graphiql-container .graphiql-tab.active { color:#1d6fd0; border-bottom-color:rgba(29,111,208,.6); }
844
+ [data-theme="light"] .graphiql-container input[type=text], [data-theme="light"] .graphiql-container textarea { background:rgba(255,255,255,.9); color:#16233c; border-color:rgba(60,110,180,.2); }
845
+ /* ── explorer-specific glass (reuses agent tokens) ── */
846
+ .graphiql-panel { display:flex; flex-direction:column; min-width:0; min-height:0; border:1px solid rgba(112,168,228,.18); border-radius:.9rem; background:linear-gradient(160deg, rgba(16,38,70,.45), rgba(8,20,42,.5)); box-shadow:inset 0 1px rgba(255,255,255,.05), 0 18px 50px rgba(0,0,0,.24); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%); overflow:hidden; animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both; }
847
+ #graphiql{flex:1; min-height:0}
848
+ .graphiql-container{height:100% !important}
849
+ .panel-head{padding:.6rem .85rem; font-size:.6rem; letter-spacing:.13em; text-transform:uppercase; color:#5f7b9c; border-bottom:1px solid rgba(112,168,228,.12); display:flex; justify-content:space-between; align-items:center; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
850
+ .ai-hint{font-size:.6rem; color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
851
+ .ops{padding:.6rem .7rem; display:flex; flex-direction:column; gap:.4rem}
852
+ .ops .op{padding:.5rem .65rem; border:1px solid rgba(112,168,228,.17); border-radius:.55rem; background:rgba(8,25,48,.6); box-shadow:inset 0 1px rgba(255,255,255,.04); cursor:pointer; display:flex; justify-content:space-between; align-items:center; gap:.5rem; transition:border-color .14s, background .14s, transform .14s; animation:agent-rise .25s cubic-bezier(.22,1,.36,1) both}
853
+ .ops .op:hover{border-color:rgba(84,183,255,.5); background:rgba(84,183,255,.1); transform:translateY(-1px)}
854
+ .ops .op b{font-size:.66rem; color:#dcecff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-weight:600}
855
+ .ops .op > span{font-size:.52rem; padding:.14rem .45rem; border-radius:999px; background:rgba(84,183,255,.15); color:#8ed6ff; border:1px solid rgba(84,183,255,.25); font-weight:700; letter-spacing:.06em; text-transform:uppercase; flex:0 0 auto}
856
+ .ops .op.mutation > span{background:rgba(243,110,184,.15); color:#fda4c7; border-color:rgba(243,110,184,.25)}
857
+ .history{padding:.5rem .7rem; border-top:1px solid rgba(112,168,228,.12); max-height:9rem; overflow:auto}
858
+ .history div{font-size:.6rem; padding:.35rem .5rem; border-radius:.45rem; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:#9fb3d0; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
859
+ .history div:hover{background:rgba(84,183,255,.1)}
860
+ /* Type details */
861
+ .type-details{padding:0 .7rem .6rem; display:flex; flex-direction:column; gap:.4rem; max-height:22rem; overflow:auto}
862
+ .type-details details{border:1px solid rgba(112,168,228,.18); border-radius:.6rem; background:linear-gradient(160deg, rgba(16,38,70,.4), rgba(8,20,42,.5)); overflow:hidden; backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px)}
863
+ .type-details summary{cursor:pointer; padding:.5rem .65rem; list-style:none; display:flex; justify-content:space-between; align-items:center; font-size:.66rem; color:#dcecff; font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-weight:700}
864
+ .type-details summary::-webkit-details-marker{display:none}
865
+ .type-details summary:hover{background:rgba(84,183,255,.07)}
866
+ .type-details .type-kind{font-size:.5rem; padding:.12rem .4rem; border-radius:999px; background:rgba(159,123,255,.15); color:#c4b5fd; border:1px solid rgba(139,92,246,.3); font-weight:800; letter-spacing:.08em; text-transform:uppercase}
867
+ .type-details .type-fields{padding:0 .6rem .5rem; display:flex; flex-direction:column; gap:.28rem}
868
+ .type-details .type-field{font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.6rem; padding:.32rem .5rem; border-radius:.45rem; background:rgba(0,0,0,.28); color:#a6bfd8; display:flex; justify-content:space-between; gap:.5rem; border:1px solid rgba(112,168,228,.08)}
869
+ .type-details .type-field b{color:#8ed6ff; font-weight:600}
870
+ .type-details .type-field .hljs{background:transparent; padding:0}
871
+ .type-details .type-desc{font-size:.58rem; color:#5f7b9c; padding:0 .65rem .5rem; line-height:1.5}
872
+ .type-filter{margin:.45rem .7rem 0; padding:.42rem .6rem; border-radius:.5rem; border:1px solid rgba(112,168,228,.22); background:rgba(20,46,79,.32); color:#e5f4ff; font-size:.62rem; width:calc(100% - 1.4rem); outline:none; transition:border-color .14s, box-shadow .14s}
873
+ .type-filter::placeholder{color:#536c8c}
874
+ .type-filter:focus{border-color:rgba(84,183,255,.55); box-shadow:0 0 0 2px rgba(84,183,255,.15)}
875
+ /* FAB + chat popup (agent glass) */
876
+ .fab{position:fixed; right:1.25rem; bottom:1.25rem; width:3.4rem; height:3.4rem; border-radius:50%; border:1px solid rgba(84,183,255,.42); background:linear-gradient(135deg, rgba(38,130,225,.75), rgba(90,67,183,.65)); color:#fff; font-size:1.25rem; font-weight:800; cursor:pointer; box-shadow:0 0 24px rgba(58,149,255,.4), 0 10px 30px rgba(0,0,0,.4); display:grid; place-items:center; z-index:30; transition:transform .15s, filter .15s; animation:agent-rise .3s cubic-bezier(.22,1,.36,1) both}
877
+ .fab:hover{transform:translateY(-2px); filter:brightness(1.12)}
878
+ .fab:active{transform:scale(.94)}
879
+ .ai-popup{position:fixed; right:1.25rem; bottom:5.4rem; width:24rem; max-width:calc(100vw - 2rem); height:32rem; max-height:70vh; border:1px solid rgba(99,189,255,.28); border-radius:.9rem; background:linear-gradient(160deg, rgba(12,34,64,.96), rgba(6,16,36,.98)); box-shadow:inset 0 1px rgba(255,255,255,.06), 0 30px 90px rgba(0,0,0,.6), 0 0 40px rgba(84,183,255,.12); display:flex; flex-direction:column; overflow:hidden; z-index:30; backdrop-filter:blur(22px) saturate(140%); -webkit-backdrop-filter:blur(22px) saturate(140%); animation:agent-rise .25s cubic-bezier(.22,1,.36,1) both}
880
+ .ai-popup[hidden]{display:none !important}
881
+ .ai-popup-head{padding:.6rem .8rem; display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid rgba(112,168,228,.14); font-size:.62rem; letter-spacing:.12em; text-transform:uppercase; color:#5f7b9c; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
882
+ .ai-popup-head button{border:1px solid rgba(112,168,228,.24); background:rgba(84,183,255,.08); color:#9fd4ff; width:1.7rem; height:1.7rem; border-radius:.45rem; cursor:pointer; transition:border-color .14s, color .14s}
883
+ .ai-popup-head button:hover{border-color:rgba(84,183,255,.55); color:#fff}
884
+ .ai-chat{flex:1; overflow:auto; padding:.75rem; display:flex; flex-direction:column; gap:.55rem}
885
+ .ai-chat .msg{display:flex; flex-direction:column; max-width:88%; gap:.3rem; animation:agent-rise .25s cubic-bezier(.22,1,.36,1) both}
886
+ .ai-chat .msg.user{align-self:flex-end; align-items:flex-end}
887
+ .ai-chat .msg.ai{align-self:flex-start; align-items:flex-start}
888
+ .ai-chat .bubble{padding:.6rem .8rem; border:1px solid rgba(112,168,228,.22); border-radius:.8rem; color:#dcecff; background:linear-gradient(160deg, rgba(16,38,70,.5), rgba(8,20,42,.56)); font-size:.78rem; line-height:1.65; white-space:pre-wrap; overflow-wrap:anywhere; word-break:break-word; box-shadow:inset 0 1px rgba(255,255,255,.05), 0 14px 42px rgba(0,0,0,.2); backdrop-filter:blur(18px) saturate(135%); -webkit-backdrop-filter:blur(18px) saturate(135%)}
889
+ .ai-chat .user .bubble{border-color:rgba(84,183,255,.42); background:linear-gradient(160deg, rgba(22,70,122,.58), rgba(46,41,112,.5)); color:#eaf7ff}
890
+ .ai-chat .bubble pre{margin:.4rem 0 0; padding:.55rem .7rem; border-radius:.55rem; background:rgba(0,0,0,.32); border:1px solid rgba(112,168,228,.12); font-family:ui-monospace, SFMono-Regular, Consolas, monospace; font-size:.64rem; overflow:auto; white-space:pre-wrap}
891
+ .ai-chat .bubble pre .hljs{background:transparent; padding:0}
892
+ .ai-chat .insert{margin-top:.4rem; padding:.28rem .6rem; border-radius:.45rem; border:1px solid rgba(84,183,255,.4); background:rgba(84,183,255,.12); color:#9fd4ff; font-size:.58rem; font-weight:700; cursor:pointer; transition:border-color .14s, background .14s, transform .14s; font-family:ui-monospace, SFMono-Regular, Consolas, monospace}
893
+ .ai-chat .insert:hover{border-color:rgba(84,183,255,.75); color:#fff; background:rgba(40,137,235,.28); transform:translateY(-1px)}
894
+ .ai-chat .typing{font-size:.6rem; color:#6e8aaa; font-style:italic}
895
+ .ai-form{padding:.65rem; border-top:1px solid rgba(112,168,228,.14); display:flex; gap:.5rem; align-items:flex-end}
896
+ .ai-form textarea{flex:1; min-height:2.4rem; max-height:6rem; border-radius:.55rem; border:1px solid rgba(112,168,228,.24); background:rgba(255,255,255,.05); color:#e8f2ff; padding:.5rem .65rem; font-size:.76rem; resize:none; outline:none; transition:border-color .14s, box-shadow .14s}
897
+ .ai-form textarea::placeholder{color:#5b7392}
898
+ .ai-form textarea:focus{border-color:rgba(84,183,255,.6); box-shadow:0 0 0 3px rgba(84,183,255,.14)}
899
+ .ai-form button{padding:.55rem 1rem; border:1px solid rgba(84,183,255,.55); border-radius:.55rem; color:#fff; background:linear-gradient(135deg, rgba(38,130,225,.75), rgba(90,67,183,.65)); box-shadow:0 8px 24px rgba(84,183,255,.28); font-size:.7rem; font-weight:800; cursor:pointer; white-space:nowrap; transition:transform .15s, filter .15s}
900
+ .ai-form button:hover:not(:disabled){filter:brightness(1.1); transform:translateY(-1px)}
901
+ .ai-form button:disabled{opacity:.4; cursor:not-allowed}
902
+ /* Theme toggle */
903
+ .theme-toggle{display:grid; place-items:center; width:2.1rem; height:2.1rem; padding:0; border:1px solid rgba(112,168,228,.24); border-radius:.6rem; color:#8fb8e4; background:rgba(84,183,255,.08); font-size:.9rem; line-height:1; cursor:pointer; transition:border-color .15s, color .15s, background .15s, transform .15s}
904
+ .theme-toggle:hover{border-color:rgba(84,183,255,.55); color:#bfe9ff; background:rgba(84,183,255,.16); transform:translateY(-1px)}
905
+ /* Light theme overrides */
906
+ [data-theme="light"] body{background:#eef3fb; color:#0b1220}
907
+ [data-theme="light"] .agent-shell{background:#eef3fb}
908
+ [data-theme="light"] .agent-shell::before{opacity:.25; background-image:linear-gradient(rgba(30,80,160,.06) 1px, transparent 1px), linear-gradient(90deg, rgba(30,80,160,.06) 1px, transparent 1px)}
909
+ [data-theme="light"] .agent-aurora i{opacity:.22; mix-blend-mode:multiply}
910
+ [data-theme="light"] .agent-header{background:linear-gradient(180deg, rgba(255,255,255,.9), rgba(235,242,252,.8)); border-bottom-color:rgba(60,110,180,.18)}
911
+ [data-theme="light"] .agent-brand-name{color:#0b1220}
912
+ [data-theme="light"] .agent-brand-sub{color:#5a6b84}
913
+ [data-theme="light"] .graphiql-panel{background:linear-gradient(160deg, rgba(255,255,255,.7), rgba(240,246,255,.75)); border-color:rgba(60,110,180,.16); box-shadow:inset 0 1px rgba(255,255,255,.6), 0 18px 50px rgba(20,50,100,.12)}
914
+ [data-theme="light"] .panel-head{color:#4a5c78; border-bottom-color:rgba(60,110,180,.14)}
915
+ [data-theme="light"] .ops .op{background:rgba(255,255,255,.72); border-color:rgba(60,110,180,.16); box-shadow:inset 0 1px rgba(255,255,255,.6)}
916
+ [data-theme="light"] .ops .op b{color:#0b1220}
917
+ [data-theme="light"] .ops .op:hover{background:rgba(84,183,255,.14)}
918
+ [data-theme="light"] .type-details details{background:rgba(255,255,255,.72); border-color:rgba(60,110,180,.16)}
919
+ [data-theme="light"] .type-details summary{color:#0b1220}
920
+ [data-theme="light"] .type-details .type-field{background:rgba(220,232,250,.6); color:#3a4c68; border-color:rgba(60,110,180,.1)}
921
+ [data-theme="light"] .type-details .type-field b{color:#1d6fd0}
922
+ [data-theme="light"] .ai-hint, [data-theme="light"] .type-details .type-desc{color:#5a6b84}
923
+ [data-theme="light"] .history div{color:#3a4c68}
924
+ [data-theme="light"] .ai-popup{background:linear-gradient(160deg, rgba(255,255,255,.96), rgba(238,245,255,.97)); border-color:rgba(60,110,180,.2)}
925
+ [data-theme="light"] .ai-chat .bubble{background:rgba(240,246,255,.8); color:#16233c; border-color:rgba(60,110,180,.18)}
926
+ [data-theme="light"] .ai-chat .user .bubble{background:linear-gradient(160deg, rgba(84,183,255,.35), rgba(120,110,230,.3)); color:#0b1220}
927
+ [data-theme="light"] .ai-chat .bubble pre{background:rgba(220,232,250,.7)}
928
+ [data-theme="light"] .type-filter{background:rgba(255,255,255,.8); color:#0b1220; border-color:rgba(60,110,180,.2)}
929
+ @media (max-width: 960px){ .ai-popup{right:.75rem; left:.75rem; width:auto} }
930
+ </style>
931
+ </head>
932
+ <body class="graphiql-dark">
933
+ <div class="agent-shell">
934
+ <div class="agent-aurora"><i></i><i></i><i></i></div>
935
+
936
+ <header class="agent-header">
937
+ <div class="agent-header-inner">
938
+ <span class="agent-brand">
939
+ <span class="agent-brand-badge"><span class="agent-brand-dot"></span></span>
940
+ <span class="agent-brand-text">
941
+ <span class="agent-brand-name">${escapeHtml(appName)} <em>GraphQL</em></span>
942
+ <span class="agent-brand-sub">${escapeHtml(endpoint)} · ${introspection ? 'introspection on' : 'introspection off'}</span>
943
+ </span>
944
+ </span>
945
+ <span class="agent-spacer"></span>
946
+ <span class="agent-status"><span class="agent-status-dot"></span>Live</span>
947
+ <span class="agent-select-wrap"><span class="agent-select-label">POST</span><span class="agent-select-label" style="color:#9fd4ff">${escapeHtml(endpoint)}</span></span>
948
+ <button id="theme-toggle" class="theme-toggle" aria-label="Toggle theme" title="Toggle dark/light">🌙</button>
949
+ </div>
950
+ <div class="agent-urlbar">
951
+ <span class="agent-urlbar-lock" title="Secure connection">🔒</span>
952
+ <span class="agent-urlbar-input">
953
+ <input class="agent-urlbar-field" id="urlbar" type="text" aria-label="GraphQL endpoint URL" spellcheck="false" />
954
+ </span>
955
+ <button class="agent-urlbar-btn" id="urlbar-copy" aria-label="Copy URL" title="Copy URL">📋</button>
956
+ <button class="agent-urlbar-btn" id="urlbar-refresh" aria-label="Reload explorer" title="Reload">↻</button>
957
+ <span class="agent-urlbar-note">POST</span>
958
+ </div>
959
+ </header>
960
+
961
+ <main class="agent-workspace">
962
+ <section class="agent-main graphiql-panel">
963
+ <div class="panel-head"><span>Explorer</span><span style="opacity:.6">GraphiQL 3</span></div>
964
+ <div id="graphiql">Loading…</div>
965
+ </section>
966
+
967
+ <aside class="agent-side">
968
+ <div class="panel-head" style="border-radius:.6rem .6rem 0 0"><span>Available Operations</span><span style="opacity:.6">${operations.length ? operations.length + ' fields' : 'no schema'}</span></div>
969
+ <div class="ops" id="ops">
970
+ ${operations.length ? operations.map(op => `
971
+ <div class="op ${op.type === 'Mutation' ? 'mutation' : ''}" data-query="${escapeHtml(op.example)}">
972
+ <b>${escapeHtml(op.field)}${op.args ? `<span style="font-weight:400; opacity:.7">(${escapeHtml(op.args)})</span>` : ''}</b>
973
+ <span>${op.type}</span>
974
+ </div>`).join('') : `<div class="ai-hint" style="padding:8px">No typedefs discovered yet. <b>hello</b> is always available — try <code>{ hello }</code> below.<br>Add more: <code>npx nexus make:subgraph post --app backend</code> (creates a real Post model).<br><br>SDL preview:<br><pre style="white-space:pre-wrap; font-size:11px; opacity:.7">${escapeHtml(rawSdl.slice(0,600) || 'empty')}</pre></div>`}
975
+ </div>
976
+ <div class="ai-hint" style="padding:0 .8rem .6rem">Click a field to insert its example. Open <b>Docs</b> in GraphiQL (left) to browse full schema. Builtin: <code>hello</code> + <code>ping(message)</code> always visible; added subgraphs appear live via introspection.</div>
977
+
978
+ <div class="panel-head" style="margin-top:.4rem; border-radius:.6rem .6rem 0 0"><span>Type Details</span><span style="opacity:.6" id="types-count">loading…</span></div>
979
+ <input class="type-filter" id="type-filter" placeholder="Filter types/fields…" />
980
+ <div class="type-details" id="types"><div class="ai-hint" style="padding:8px">Loading complete schema via introspection…</div></div>
981
+
982
+ </aside>
983
+ </main>
984
+ </div>
985
+
986
+ <!-- AI Assistant FAB + Chat Popup -->
987
+ <button id="ai-fab" class="fab" aria-label="AI Assistant" title="AI Assistant">✦</button>
988
+ <div id="ai-popup" class="ai-popup" hidden>
989
+ <div class="ai-popup-head"><span>AI Assistant <span style="opacity:.6" id="ai-model-label"></span></span><button id="ai-close" aria-label="Close">×</button></div>
990
+ <div id="ai-chat" class="ai-chat"><div class="ai-hint" style="padding:8px">Hi! Describe what you want in GraphQL and I’ll generate a query from your live schema. Try: “Show posts with authors”.</div></div>
991
+ <form id="ai-form" class="ai-form">
992
+ <textarea id="ai-prompt" placeholder="Ask about your schema, e.g. 'Show the 5 latest posts with author names'" rows="2"></textarea>
993
+ <button type="submit" id="ai-send">Send</button>
994
+ </form>
995
+ <div id="ai-status" class="ai-hint" style="padding:6px 12px"></div>
996
+ </div>
997
+
998
+ <script src="${CDN.hljsJs}"></script>
999
+ <script crossorigin src="${CDN.react}"></script>
1000
+ <script crossorigin src="${CDN.reactDom}"></script>
1001
+ <script src="${CDN.graphiqlJs}"></script>
1002
+ <script>
1003
+ const endpoint = ${JSON.stringify(endpoint)};
1004
+ const sdlPreview = ${JSON.stringify(sdlPreview)};
1005
+ const rawSdlClient = ${JSON.stringify(rawSdl.slice(0,4000))};
1006
+ const STORAGE_KEY = 'nexus-graphql-history';
1007
+ const THEME_KEY = 'nexus-theme';
1008
+
1009
+ // \u2192 Editable/copyable URL bar (below the header) — shows the live GraphQL endpoint.
1010
+ const urlbar = document.getElementById('urlbar');
1011
+ const copyBtn = document.getElementById('urlbar-copy');
1012
+ const refreshBtn = document.getElementById('urlbar-refresh');
1013
+ if (urlbar) {
1014
+ const fullUrl = window.location.origin + endpoint;
1015
+ urlbar.value = fullUrl;
1016
+ urlbar.addEventListener('focus', function () { this.select(); });
1017
+ if (copyBtn) {
1018
+ copyBtn.addEventListener('click', function () {
1019
+ const val = urlbar.value;
1020
+ function done() {
1021
+ copyBtn.classList.add('copied');
1022
+ copyBtn.textContent = '✓';
1023
+ setTimeout(function () {
1024
+ copyBtn.classList.remove('copied');
1025
+ copyBtn.textContent = '📋';
1026
+ }, 1200);
1027
+ }
1028
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1029
+ navigator.clipboard.writeText(val).then(done, function () { urlbar.select(); try { document.execCommand('copy'); done(); } catch (e) {} });
1030
+ } else {
1031
+ urlbar.select();
1032
+ try { document.execCommand('copy'); done(); } catch (e) {}
1033
+ }
1034
+ });
1035
+ }
1036
+ if (refreshBtn) refreshBtn.addEventListener('click', function () { location.reload(); });
1037
+ // After executing a query, append it (URL-encoded) so the bar stays in sync.
1038
+ // Mutations also append a fresh csrf token so the copied URL works when the
1039
+ // nexus_csrf cookie is present (same browser/device).
1040
+ window.__updateUrlbar = function (q, token) {
1041
+ if (!urlbar || !q || !q.trim()) return;
1042
+ if (q.indexOf('__schema') !== -1 || q.indexOf('__type') !== -1) return; // ignore introspection
1043
+ let url = window.location.origin + endpoint + '?query=' + encodeURIComponent(q);
1044
+ if (token && /^\s*mutation\b/.test(q)) url += '&csrf=' + encodeURIComponent(token);
1045
+ urlbar.value = url;
1046
+ };
1047
+ }
1048
+
1049
+ let csrfToken = null;
1050
+
1051
+ // \u2190 History button in GraphiQL sidebar
1052
+ const graphiqlSidebar = document.querySelector('.graphiql-container .graphiql-sidebar')
1053
+ if (graphiqlSidebar) {
1054
+ const historyBtn = document.createElement('button')
1055
+ historyBtn.className = 'graphiql-history-btn'
1056
+ historyBtn.setAttribute('aria-label', 'Toggle history')
1057
+ historyBtn.textContent = 'History'
1058
+ graphiqlSidebar.appendChild(historyBtn)
1059
+ historyBtn.addEventListener('click', function() {
1060
+ const historyPanel = document.getElementById('history')
1061
+ if (historyPanel) {
1062
+ historyPanel.hidden = !historyPanel.hidden
1063
+ this.classList.toggle('active', !historyPanel.hidden)
1064
+ }
1065
+ })
1066
+ }
1067
+
1068
+ // \u2192 Theme toggle (dark default, persisted, respects prefers-color-scheme) ──
1069
+ // Also flips body.graphiql-dark / graphiql-light so GraphiQL's own CDN
1070
+ // dark palette applies (it ignores [data-theme] otherwise).
1071
+ function applyTheme(t){
1072
+ try{ localStorage.setItem(THEME_KEY, t); }catch(e){}
1073
+ document.documentElement.setAttribute('data-theme', t);
1074
+ document.body.classList.toggle('graphiql-dark', t === 'dark');
1075
+ document.body.classList.toggle('graphiql-light', t === 'light');
1076
+ const b = document.getElementById('theme-toggle');
1077
+ if(b) b.textContent = t === 'light' ? '☀️' : '🌙';
1078
+ }
1079
+ (function initTheme(){
1080
+ let saved = null;
1081
+ try{ saved = localStorage.getItem(THEME_KEY); }catch(e){}
1082
+ if(saved === 'light' || saved === 'dark'){ applyTheme(saved); return; }
1083
+ if(window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches){ applyTheme('light'); return; }
1084
+ applyTheme('dark');
1085
+ })();
1086
+ const themeBtn = document.getElementById('theme-toggle');
1087
+ if(themeBtn) themeBtn.addEventListener('click', function(){
1088
+ const cur = document.documentElement.getAttribute('data-theme');
1089
+ applyTheme(cur === 'light' ? 'dark' : 'light');
1090
+ });
1091
+
1092
+ // ── highlight.js helper (mirrors Agent.tsx highlightCode) ──
1093
+ function escapeHtml(s){ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
1094
+ function highlightCode(code, lang){
1095
+ try{
1096
+ var hl = window.hljs;
1097
+ if(hl){
1098
+ if(lang && hl.getLanguage(lang)) return hl.highlight(code, { language: lang }).value;
1099
+ return hl.highlightAuto(code).value;
1100
+ }
1101
+ }catch(e){}
1102
+ return escapeHtml(code);
1103
+ }
1104
+ async function ensureCsrf(){
1105
+ if(csrfToken) return csrfToken;
1106
+ try{
1107
+ const r = await fetch('/csrf-token', { credentials: 'include' });
1108
+ if(r.ok){ const j = await r.json(); if(j && j.token) csrfToken = j.token; }
1109
+ if(!csrfToken){
1110
+ const m = document.cookie.match(/(?:^|; )csrf_token=([^;]+)/);
1111
+ if(m) csrfToken = decodeURIComponent(m[1]);
1112
+ }
1113
+ if(!csrfToken){
1114
+ const m2 = document.cookie.match(/(?:^|; )XSRF-TOKEN=([^;]+)/);
1115
+ if(m2) csrfToken = decodeURIComponent(m2[1]);
1116
+ }
1117
+ }catch(e){}
1118
+ return csrfToken;
1119
+ }
1120
+ // prefetch csrf in background
1121
+ ensureCsrf();
1122
+
1123
+ async function fetcher(params, opts) {
1124
+ try {
1125
+ const token = await ensureCsrf();
1126
+ const headers = { 'Content-Type': 'application/json', ...(opts && opts.headers ? opts.headers : {}) };
1127
+ if(token) headers['X-CSRF-Token'] = token;
1128
+ // Mutations require a CSRF token passed as an argument (body.csrf).
1129
+ const payload = Object.assign({}, params);
1130
+ if (token && params && params.query && /^\s*mutation\b/.test(params.query)) {
1131
+ payload.csrf = token;
1132
+ if (payload.variables && typeof payload.variables === 'object') {
1133
+ const vars = Object.assign({}, payload.variables);
1134
+ vars.csrf = token;
1135
+ payload.variables = vars;
1136
+ }
1137
+ }
1138
+ return fetch(endpoint, {
1139
+ method: 'POST',
1140
+ credentials: 'include',
1141
+ headers: headers,
1142
+ body: JSON.stringify(payload),
1143
+ }).then(function(r){
1144
+ if(!r.ok) throw new Error('GraphiQL request failed: ' + r.status);
1145
+ if (typeof window.__updateUrlbar === 'function' && params && params.query) window.__updateUrlbar(params.query, token);
1146
+ return r.json();
1147
+ });
1148
+ } catch(e) {
1149
+ console.error('GraphiQL fetcher error:', e);
1150
+ return Promise.reject(e);
1151
+ }
1152
+ }
1153
+
1154
+ function saveHistory(query) {
1155
+ if (!query || !query.trim()) return;
1156
+ try {
1157
+ const list = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
1158
+ const next = [query, ...list.filter(q => q !== query)].slice(0, 20);
1159
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
1160
+ renderHistory();
1161
+ } catch {}
1162
+ }
1163
+ function renderHistory() {
1164
+ const el = document.getElementById('history');
1165
+ if (!el) return;
1166
+ try {
1167
+ const list = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
1168
+ el.innerHTML = list.length ? list.map(q => \`<div title="\${q.replace(/"/g,'&quot;')}">\${q.slice(0,80)}</div>\`).join('') : '<div style="opacity:.5">No history yet</div>';
1169
+ el.querySelectorAll('div').forEach(div => div.addEventListener('click', () => {
1170
+ const q = div.getAttribute('title');
1171
+ if (window._graphiql) window._graphiql.setQuery(q);
1172
+ }));
1173
+ } catch {}
1174
+ }
1175
+
1176
+ // Mount GraphiQL
1177
+ const defaultQuery = ${JSON.stringify(defaultQuery)};
1178
+ const root = ReactDOM.createRoot(document.getElementById('graphiql'));
1179
+ const graphiql = React.createElement(GraphiQL, {
1180
+ fetcher,
1181
+ defaultQuery,
1182
+ onEditQuery: (q) => saveHistory(q),
1183
+ isHeadersEditorEnabled: true,
1184
+ shouldPersistHeaders: true,
1185
+ });
1186
+ root.render(graphiql);
1187
+ // Expose for AI/history/ops
1188
+ window._graphiql = {
1189
+ setQuery: (q) => {
1190
+ try { localStorage.setItem('graphiql:query', q); location.reload(); } catch { alert(q); }
1191
+ }
1192
+ };
1193
+ // Click operation → insert example
1194
+ function bindOps() {
1195
+ document.querySelectorAll('#ops .op').forEach(el => {
1196
+ el.addEventListener('click', () => {
1197
+ const q = el.getAttribute('data-query');
1198
+ if (q) {
1199
+ try { localStorage.setItem('graphiql:query', q); location.reload(); } catch {}
1200
+ }
1201
+ });
1202
+ });
1203
+ }
1204
+ bindOps();
1205
+ renderHistory();
1206
+
1207
+ // Live introspection refresh — discovers hello + any added subgraph and renders full type details
1208
+ (function refreshOpsFromIntrospection(){
1209
+ const INTROSPECTION_QUERY = '{ __schema { queryType { name fields { name description args { name description type { name kind ofType { name kind ofType { name kind } } } } type { name kind ofType { name kind ofType { name kind } } } } } mutationType { name fields { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind } } } } subscriptionType { name fields { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind } } } } types { name kind description fields(includeDeprecated:false) { name description args { name description type { name kind ofType { name kind } } } type { name kind ofType { name kind ofType { name kind } } } } inputFields { name description type { name kind ofType { name kind } } } enumValues { name description } } } }';
1210
+ function typeToString(t){
1211
+ if(!t) return '';
1212
+ if(t.kind==='NON_NULL') return typeToString(t.ofType) + '!';
1213
+ if(t.kind==='LIST') return '[' + typeToString(t.ofType) + ']';
1214
+ return t.name || '';
1215
+ }
1216
+ function escapeTd(s){ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
1217
+ ensureCsrf().then(function(token){
1218
+ const headers = {'Content-Type':'application/json'};
1219
+ if(token) headers['X-CSRF-Token'] = token;
1220
+ return fetch(endpoint, { method:'POST', credentials:'include', headers: headers, body: JSON.stringify({ query: INTROSPECTION_QUERY }) });
1221
+ })
1222
+ .then(function(r){ return r.ok ? r.json() : null; })
1223
+ .then(function(json){
1224
+ const schema = json && json.data && json.data.__schema;
1225
+ if (!schema) return;
1226
+ const qFields = (schema.queryType && schema.queryType.fields) || [];
1227
+ const mFields = (schema.mutationType && schema.mutationType.fields) || [];
1228
+ const sFields = (schema.subscriptionType && schema.subscriptionType.fields) || [];
1229
+ // Ops panel
1230
+ if (qFields.length || mFields.length || sFields.length){
1231
+ const opsEl = document.getElementById('ops');
1232
+ if (opsEl){
1233
+ const currentCount = opsEl.querySelectorAll('.op').length;
1234
+ const total = qFields.length + mFields.length + sFields.length;
1235
+ if (total > currentCount){
1236
+ function fieldToExample(type, name, args){
1237
+ const argStr = args && args.length ? '(' + args.map(function(a){ return a.name + ': "demo"'; }).join(', ') + ')' : '';
1238
+ if (type==='Query'){ if (name==='hello') return 'query Demo {\\n hello\\n}'; if (name==='ping') return 'query Demo {\\n ping(message: "hi")\\n}'; return 'query Demo {\\n ' + name + argStr + '\\n}'; }
1239
+ if (type==='Mutation') return 'mutation Demo {\\n ' + name + argStr + '\\n}';
1240
+ return 'subscription Demo {\\n ' + name + argStr + '\\n}';
1241
+ }
1242
+ const rows = [];
1243
+ qFields.forEach(function(f){ rows.push({type:'Query', field:f.name, args:(f.args||[]).map(function(a){return a.name;}).join(', '), typeStr: typeToString(f.type), desc: f.description, example: fieldToExample('Query', f.name, f.args)}); });
1244
+ mFields.forEach(function(f){ rows.push({type:'Mutation', field:f.name, args:(f.args||[]).map(function(a){return a.name;}).join(', '), typeStr: typeToString(f.type), desc: f.description, example: fieldToExample('Mutation', f.name, f.args)}); });
1245
+ sFields.forEach(function(f){ rows.push({type:'Subscription', field:f.name, args:(f.args||[]).map(function(a){return a.name;}).join(', '), typeStr: typeToString(f.type), desc: f.description, example: fieldToExample('Subscription', f.name, f.args)}); });
1246
+ const head = opsEl.previousElementSibling;
1247
+ if (head) head.lastElementChild.textContent = rows.length + ' fields';
1248
+ opsEl.innerHTML = rows.map(function(op){
1249
+ const ret = op.typeStr ? '<span style="opacity:.6; font-weight:400">: ' + escapeTd(op.typeStr) + '</span>' : '';
1250
+ const desc = op.desc ? '<div style="font-size:10px; opacity:.6; margin-top:2px">' + escapeTd(op.desc.slice(0,120)) + '</div>' : '';
1251
+ return '<div class="op ' + (op.type==='Mutation'?'mutation':'') + '" data-query="' + op.example.replace(/"/g,'&quot;') + '"><div><b>' + escapeTd(op.field) + (op.args ? '<span style="font-weight:400; opacity:.7">(' + escapeTd(op.args) + ')</span>' : '') + ret + '</b>' + desc + '</div><span>' + op.type + '</span></div>';
1252
+ }).join('');
1253
+ bindOps();
1254
+ const hint = document.querySelector('.ops + .ai-hint');
1255
+ if (hint) hint.textContent = 'Live from introspection — includes hello + discovered subgraphs (posts, etc.). Click to insert.';
1256
+ }
1257
+ }
1258
+ }
1259
+ // Type details panel
1260
+ const typesEl = document.getElementById('types');
1261
+ const countEl = document.getElementById('types-count');
1262
+ const filterEl = document.getElementById('type-filter');
1263
+ if (!typesEl || !schema.types) return;
1264
+ const ignore = ['String','Int','Float','Boolean','ID','__Schema','__Type','__TypeKind','__Field','__InputValue','__EnumValue','__Directive','_Any','_Entity','_Service'];
1265
+ const userTypes = schema.types.filter(function(t){ return !t.name.startsWith('__') && !t.name.startsWith('_') && ignore.indexOf(t.name)===-1; });
1266
+ userTypes.sort(function(a,b){ return a.name.localeCompare(b.name); });
1267
+ if (countEl) countEl.textContent = userTypes.length + ' types';
1268
+ function renderTypes(filter){
1269
+ const q = (filter||'').toLowerCase();
1270
+ const filtered = q ? userTypes.filter(function(t){ return t.name.toLowerCase().indexOf(q)!==-1 || (t.fields && t.fields.some(function(f){ return f.name.toLowerCase().indexOf(q)!==-1; })); }) : userTypes;
1271
+ if (!filtered.length){ typesEl.innerHTML = '<div class="ai-hint" style="padding:8px">No types match “' + escapeTd(filter) + '”.</div>'; return; }
1272
+ typesEl.innerHTML = filtered.map(function(t){
1273
+ const kind = t.kind;
1274
+ const desc = t.description ? '<div class="type-desc">' + escapeTd(t.description.slice(0,300)) + '</div>' : '';
1275
+ let fieldsHtml = '';
1276
+ if (t.fields && t.fields.length){
1277
+ fieldsHtml = '<div class="type-fields">' + t.fields.map(function(f){
1278
+ const fType = typeToString(f.type);
1279
+ const fArgs = f.args && f.args.length ? '(' + f.args.map(function(a){ return a.name + ': ' + typeToString(a.type); }).join(', ') + ')' : '';
1280
+ const fDesc = f.description ? ' — ' + escapeTd(f.description.slice(0,80)) : '';
1281
+ const sig = f.name + fArgs + ': ' + fType;
1282
+ return '<div class="type-field"><span><code class="hljs">' + highlightCode(sig, 'graphql') + '</code><span style="opacity:.6">' + fDesc + '</span></span><span style="font-size:10px; opacity:.5; cursor:pointer" data-field-query="' + escapeTd(f.name) + '">→</span></div>';
1283
+ }).join('') + '</div>';
1284
+ } else if (t.inputFields && t.inputFields.length){
1285
+ fieldsHtml = '<div class="type-fields">' + t.inputFields.map(function(f){ return '<div class="type-field"><span><code class="hljs">' + highlightCode(f.name + ': ' + typeToString(f.type), 'graphql') + '</code></span></div>'; }).join('') + '</div>';
1286
+ } else if (t.enumValues && t.enumValues.length){
1287
+ fieldsHtml = '<div class="type-fields">' + t.enumValues.map(function(v){ return '<div class="type-field"><span><b>' + escapeTd(v.name) + '</b>' + (v.description ? ' — ' + escapeTd(v.description.slice(0,60)) : '') + '</span></div>'; }).join('') + '</div>';
1288
+ }
1289
+ const open = (t.name==='Query' || t.name==='Post' || t.name==='Message') ? ' open' : '';
1290
+ return '<details' + open + '><summary><span><b>' + escapeTd(t.name) + '</b> ' + (desc ? '' : '') + '</span><span class="type-kind">' + escapeTd(kind) + '</span></summary>' + desc + fieldsHtml + '</details>';
1291
+ }).join('');
1292
+ }
1293
+ renderTypes('');
1294
+ if (filterEl){
1295
+ filterEl.addEventListener('input', function(){ renderTypes(filterEl.value); });
1296
+ }
1297
+ }).catch(function(){
1298
+ const el=document.getElementById('types'); if(el) el.innerHTML='<div class="ai-hint" style="padding:8px">Introspection failed. SDL preview:<pre style="white-space:pre-wrap; font-size:11px; opacity:.7">' + escapeTd(rawSdlClient.slice(0,800) || 'empty') + '</pre></div>';
1299
+ const c=document.getElementById('types-count'); if(c) c.textContent='—';
1300
+ });
1301
+ })();
1302
+
1303
+ // AI Assistant — FAB + chat popup (replaces old single-button generate)
1304
+ const aiFab = document.getElementById('ai-fab');
1305
+ const aiPopup = document.getElementById('ai-popup');
1306
+ const aiClose = document.getElementById('ai-close');
1307
+ const aiForm = document.getElementById('ai-form');
1308
+ const aiPrompt = document.getElementById('ai-prompt');
1309
+ const aiChat = document.getElementById('ai-chat');
1310
+ const aiStatus = document.getElementById('ai-status');
1311
+ const aiModelLabel = document.getElementById('ai-model-label');
1312
+ const CHAT_KEY = 'nexus-ai-chat';
1313
+ let aiProvider = 'auto';
1314
+ let aiModel = '';
1315
+ function loadChat(){ try{ return JSON.parse(localStorage.getItem(CHAT_KEY) || '[]'); }catch{ return []; } }
1316
+ function saveChat(list){ try{ localStorage.setItem(CHAT_KEY, JSON.stringify(list.slice(-20))); }catch{} }
1317
+ function escapeChatHtml(s){ return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
1318
+ function extractQuery(text){
1319
+ if(!text) return null;
1320
+ const m = text.match(/\`\`\`graphql([\\s\\S]*?)\`\`\`/) || text.match(/\`\`\`([\\s\\S]*?)\`\`\`/) || text.match(/\\{[\\s\\S]*\\}/);
1321
+ return m ? (m[1] || m[0]).trim() : (text.includes('{') ? text.trim() : null);
1322
+ }
1323
+ function renderChat(){
1324
+ const list = loadChat();
1325
+ if(!list.length){
1326
+ aiChat.innerHTML = '<div class="ai-hint" style="padding:8px">Hi! I can see your live schema. Ask me to generate queries, explain types, or debug errors.</div>';
1327
+ return;
1328
+ }
1329
+ aiChat.innerHTML = list.map(function(it){
1330
+ const isUser = it.role==='user';
1331
+ const safe = escapeChatHtml(it.content);
1332
+ const hasCode = !isUser && it.content.includes('{');
1333
+ const code = hasCode ? '<pre><code class="hljs">' + highlightCode((extractQuery(it.content) || it.content).slice(0,2000), 'graphql') + '</code></pre>' : '';
1334
+ const btn = hasCode ? '<button class="insert" data-query="' + escapeChatHtml(extractQuery(it.content) || '').replace(/"/g,'&quot;') + '">Insert query</button>' : '';
1335
+ return '<div class="msg ' + (isUser?'user':'ai') + '"><div class="bubble">' + (isUser? safe : safe.replace(/\\n/g,'<br>')) + code + btn + '</div></div>';
1336
+ }).join('');
1337
+ aiChat.querySelectorAll('.insert').forEach(function(b){
1338
+ b.addEventListener('click', function(){
1339
+ const q = b.getAttribute('data-query');
1340
+ if(q){ try{ localStorage.setItem('graphiql:query', q); location.reload(); }catch{ alert(q); } }
1341
+ });
1342
+ });
1343
+ aiChat.scrollTop = aiChat.scrollHeight;
1344
+ }
1345
+ renderChat();
1346
+ async function resolveAiConfig(){
1347
+ try{
1348
+ const s = await fetch('/admin/ai/status', {credentials:'include'}).then(function(r){ return r.ok ? r.json() : null; });
1349
+ if(s){ aiProvider = s.defaultProvider || s.autoResolvesTo || 'auto'; aiModel = s.schemaModel || ''; if(!aiModel){ const p = await fetch('/admin/ai/providers', {credentials:'include'}).then(function(r){ return r.ok ? r.json() : null; }); const en = p && p.providers && p.providers.find(function(x){ return x.enabled && x.defaultModel; }); if(en && en.defaultModel) aiModel = en.defaultModel; } }
1350
+ }catch(e){}
1351
+ if(!aiModel) aiModel = 'llama3.1:8b';
1352
+ if(aiModelLabel) aiModelLabel.textContent = '(' + aiProvider + '/' + aiModel + ')';
1353
+ }
1354
+ resolveAiConfig();
1355
+ if(aiFab && aiPopup){
1356
+ aiFab.addEventListener('click', function(){ aiPopup.hidden = !aiPopup.hidden; if(!aiPopup.hidden) { renderChat(); aiPrompt.focus(); } });
1357
+ if(aiClose) aiClose.addEventListener('click', function(){ aiPopup.hidden = true; });
1358
+ }
1359
+ function setChatTyping(on){
1360
+ let el = document.getElementById('ai-typing');
1361
+ if(on){
1362
+ if(!el){ el = document.createElement('div'); el.id='ai-typing'; el.className='typing'; el.textContent='AI is thinking…'; aiChat.appendChild(el); }
1363
+ aiChat.scrollTop = aiChat.scrollHeight;
1364
+ } else if(el){ el.remove(); }
1365
+ }
1366
+ if(aiForm){
1367
+ aiForm.addEventListener('submit', async function(e){
1368
+ e.preventDefault();
1369
+ const prompt = aiPrompt.value.trim();
1370
+ if(!prompt) return;
1371
+ const history = loadChat();
1372
+ history.push({ role:'user', content: prompt, ts: Date.now() });
1373
+ saveChat(history);
1374
+ renderChat();
1375
+ aiPrompt.value = '';
1376
+ const btn = document.getElementById('ai-send');
1377
+ if(btn) btn.disabled = true;
1378
+ if(aiStatus) aiStatus.textContent = 'Thinking…';
1379
+ setChatTyping(true);
1380
+ try{
1381
+ await resolveAiConfig();
1382
+ const messages = [{ role:'system', content:'You are a helpful GraphQL assistant. You have the live schema SDL (preview: ' + sdlPreview.slice(0,2000) + '). Answer concisely. When asked to generate a query, return the query in a \`\`\`graphql code block and briefly explain.' }];
1383
+ const recent = history.slice(-8);
1384
+ recent.forEach(function(m){ messages.push({ role: m.role, content: m.content }); });
1385
+ const csrf = await ensureCsrf();
1386
+ const aiHeaders = {'Content-Type':'application/json'};
1387
+ if(csrf) aiHeaders['X-CSRF-Token'] = csrf;
1388
+ const res = await fetch('/ai/chat/completions', { method:'POST', credentials: 'include', headers: aiHeaders, body: JSON.stringify({ model: aiModel, provider: aiProvider, messages: messages, stream:false }) });
1389
+ let text = '';
1390
+ if(res.ok){
1391
+ const data = await res.json();
1392
+ text = (data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content) || data.content || data.query || '';
1393
+ } else {
1394
+ const errText = await res.text().catch(function(){ return ''; });
1395
+ throw new Error(errText || 'AI not configured');
1396
+ }
1397
+ if(!text) text = 'I could not generate a response. Try rephrasing.';
1398
+ history.push({ role:'assistant', content: text, ts: Date.now() });
1399
+ saveChat(history);
1400
+ renderChat();
1401
+ if(aiStatus) aiStatus.textContent = 'Responded (' + aiProvider + '/' + aiModel + ')';
1402
+ // auto offer insert if contains query
1403
+ const q = extractQuery(text);
1404
+ if(q && q.includes('{') && !text.includes('I could not')){
1405
+ // highlight that query is insertable via button in bubble
1406
+ }
1407
+ } catch(err){
1408
+ const fallback = 'query AiDemo {\\n # Fallback for: ' + prompt.replace(/\\n/g,' ') + '\\n __typename\\n}';
1409
+ const msg = 'AI unavailable: ' + (err && err.message ? err.message : 'error') + '\\n\\nFallback query:\\n\`\`\`graphql\\n' + fallback + '\\n\`\`\`' ;
1410
+ history.push({ role:'assistant', content: msg, ts: Date.now() });
1411
+ saveChat(history);
1412
+ renderChat();
1413
+ if(aiStatus) aiStatus.textContent = 'AI unavailable — inserted fallback. Configure provider in Admin → AI Agents.';
1414
+ } finally {
1415
+ setChatTyping(false);
1416
+ if(btn) btn.disabled = false;
1417
+ if(aiStatus) setTimeout(function(){ aiStatus.textContent=''; }, 6000);
1418
+ }
1419
+ });
1420
+ }
1421
+ </script>
1422
+ </body>
1423
+ </html>`;
1424
+ }
1425
+
1426
+ function extractOperations(sdl: string): Array<{ type: string; field: string; args: string; example: string }> {
1427
+ const ops: Array<{ type: string; field: string; args: string; example: string }> = [];
1428
+ // Support both `type Query` and `extend type Query`, and strip federation noise like @key/@link
1429
+ const cleaned = sdl
1430
+ .replace(/extend\s+schema\s*@\s*link[^\n]*/g, '')
1431
+ .replace(/@\s*key\s*\([^)]*\)/g, '')
1432
+ .replace(/@\s*extends\b/g, '')
1433
+ .replace(/@\s*external\b/g, '');
1434
+ const typeRegex = /(?:extend\s+)?type\s+(Query|Mutation|Subscription)\s*\{([^}]*)\}/g;
1435
+ let m: RegExpExecArray | null;
1436
+ while ((m = typeRegex.exec(cleaned))) {
1437
+ const type = m[1]!;
1438
+ const body = m[2]!;
1439
+ const fieldRegex = /(\w+)\s*(\([^)]*\))?\s*:/g;
1440
+ let f: RegExpExecArray | null;
1441
+ while ((f = fieldRegex.exec(body))) {
1442
+ const field = f[1]!;
1443
+ if (field.startsWith('_')) continue;
1444
+ const args = (f[2] ?? '').replace(/\s+/g, ' ').trim();
1445
+ let example = '';
1446
+ if (type === 'Query') {
1447
+ if (field === 'hello') example = 'query Demo {\n hello\n}';
1448
+ else if (field === 'ping') example = 'query Demo {\n ping(message: "hi")\n}';
1449
+ else example = `query Demo {\n ${field}${args ? `(${args.replace(/:\s*[^,)]+/g, ': "demo"').replace(/[()]/g, '')})` : ''}\n}`;
1450
+ if (args && !['hello', 'ping'].includes(field)) {
1451
+ const demoArgs = args.replace(/(\w+)\s*:\s*[^,)]+/g, '$1: "demo"');
1452
+ example = `query Demo {\n ${field}${demoArgs}\n}`;
1453
+ }
1454
+ } else if (type === 'Mutation') {
1455
+ if (field === 'echo') example = 'mutation Demo {\n echo(input: "hello")\n}';
1456
+ else example = `mutation Demo {\n ${field}${args ? `(${args.replace(/(\w+)\s*:\s*[^,)]+/g, '$1: "demo"')})` : ''}\n}`;
1457
+ } else {
1458
+ example = `subscription Demo {\n ${field}${args ? `(${args.replace(/(\w+)\s*:\s*[^,)]+/g, '$1: "demo"')})` : ''}\n}`;
1459
+ }
1460
+ ops.push({ type, field, args: args.replace(/[()]/g, ''), example });
1461
+ }
1462
+ }
1463
+ if (ops.length === 0 && sdl.includes('hello')) {
1464
+ ops.push({ type: 'Query', field: 'hello', args: '', example: 'query Demo {\n hello\n}' });
1465
+ if (sdl.includes('ping')) ops.push({ type: 'Query', field: 'ping', args: 'message: String', example: 'query Demo {\n ping(message: "hi")\n}' });
1466
+ }
1467
+ // De-dupe by field name, keep first occurrence
1468
+ const seen = new Set<string>();
1469
+ const uniq: typeof ops = [];
1470
+ for (const o of ops) if (!seen.has(o.field)) { seen.add(o.field); uniq.push(o); }
1471
+ return uniq.slice(0, 30);
1472
+ }
1473
+
1474
+ function buildDefaultQuery(ops: ReturnType<typeof extractOperations>): string {
1475
+ if (!ops.length) return '# No schema found — try { hello } — add a subgraph with: npx nexus make:subgraph demo --app backend\n\nquery Demo {\n hello\n ping(message: "hi")\n}';
1476
+ const lines = ops.map(o => {
1477
+ if (o.field === 'hello') return ' hello';
1478
+ if (o.field === 'ping') return ' ping(message: "hi")';
1479
+ if (o.type === 'Mutation') return ` # ${o.field} — use as mutation`;
1480
+ if (o.type === 'Subscription') return ` # ${o.field} — use as subscription`;
1481
+ return ` ${o.field}`;
1482
+ });
1483
+ return `# Welcome to Nexus GraphQL Sandbox
1484
+ # Available: ${ops.map(o => o.field).join(', ')}
1485
+ # Click any operation on the right or Docs ← to explore
1486
+ # Press Ctrl+Enter to run
1487
+
1488
+ query Demo {
1489
+ ${lines.join('\n')}
1490
+ }`;
1491
+ }
1492
+
1493
+ function escapeHtml(s: string): string {
1494
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
1495
+ }