PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / trunk
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin vtrunk
1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 All 33 releases
desktop-mode / assets / css / ai-assistant.css

ai-assistant.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin trunk, at assets/css/ai-assistant.css

1,013 lines 28.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * OpenStation — AI Assistant spotlight overlay.
3 *
4 * A system-level command palette that floats above the entire shell,
5 * including the WP admin bar. Visually modeled on macOS Spotlight and
6 * Linear's command menu — frosted-glass backdrop, spring-physics panel
7 * entrance, large search input, accent-tinted header.
8 *
9 * z-index 10000 puts this above --os-z-adminbar (9991) so the
10 * overlay truly covers the full viewport.
11 *
12 * ## Desktop-theme awareness
13 *
14 * The overlay mounts on `document.body`, so it sits inside the
15 * `body.os-desktop-theme-<slug>` half of a compiled theme's
16 * doubled selector and every `--os-ui-*` token below resolves against the
17 * active theme. Each site reads `var( --os-ui-x, <the literal that was
18 * always there> )`, so an unthemed shell paints exactly as before.
19 *
20 * Getting this half-right is worse than not doing it at all: when the
21 * text tones followed the theme but the panel stayed hardcoded white,
22 * a dark theme's light `--os-ui-fg` landed on white and the results were
23 * unreadable. Any new surface added here MUST take its background,
24 * border and text from the palette — not from a literal.
25 */
26
27 /* ---------------------------------------------------------------------------
28 * Root overlay — full viewport, fades in/out
29 * ------------------------------------------------------------------------- */
30
31 .os-ai {
32 position: fixed;
33 inset: 0;
34 z-index: 10000;
35
36 display: flex;
37 align-items: flex-start;
38 justify-content: center;
39
40 /* Land the panel at ~16-18% from the top — feels like Spotlight.
41 clamp keeps it 60px from the top on very short screens and caps
42 at 180px on tall displays so it never feels too low. */
43 padding-top: clamp( 60px, 16vh, 180px );
44 padding-inline: 16px;
45 padding-bottom: 40px;
46
47 opacity: 0;
48 pointer-events: none;
49 transition: opacity 0.18s ease;
50 }
51
52 /* [hidden] keeps the element out of the a11y tree between sessions.
53 Only removed while the overlay is visually visible (open or animating). */
54 .os-ai[hidden] {
55 display: none;
56 }
57
58 .os-ai.is-open {
59 opacity: 1;
60 pointer-events: auto;
61 }
62
63 /* ---------------------------------------------------------------------------
64 * Backdrop — frosted glass
65 * ------------------------------------------------------------------------- */
66
67 .os-ai__backdrop {
68 position: absolute;
69 inset: 0;
70 background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.36 ) );
71 /* Desktop-theme SCRIM texture slot: unset resolves to none. */
72 background-image: var( --os-ui-scrim-image, none );
73 background-repeat: var( --os-ui-scrim-image-repeat, repeat );
74 background-size: var( --os-ui-scrim-image-size, auto );
75 background-position: var( --os-ui-scrim-image-position, center );
76 backdrop-filter: blur( 8px ) saturate( 0.75 );
77 -webkit-backdrop-filter: blur( 8px ) saturate( 0.75 );
78 /* Non-interactive: clicks in the dimmed area fall through to the overlay
79 container, which closes the assistant (click-outside-to-dismiss). */
80 pointer-events: none;
81 }
82
83 /* ---------------------------------------------------------------------------
84 * Panel — the visible card
85 * ------------------------------------------------------------------------- */
86
87 .os-ai__panel {
88 position: relative; /* sits above the backdrop */
89 width: 100%;
90 max-width: 600px;
91
92 /* Longhand on purpose: the DIALOG texture slot below owns
93 background-image, and the shorthand would reset it. */
94 background-color: var(
95 --os-ai-panel-bg,
96 var( --os-ui-surface, rgba( 255, 255, 255, 0.97 ) )
97 );
98 /* Desktop-theme DIALOG texture slot: unset resolves to none. */
99 background-image: var( --os-ui-dialog-bg-image, none );
100 background-repeat: var( --os-ui-dialog-bg-image-repeat, repeat );
101 background-size: var( --os-ui-dialog-bg-image-size, auto );
102 background-position: var( --os-ui-dialog-bg-image-position, center );
103 color: var( --os-ui-fg, #1d2327 );
104 border-radius: 16px;
105 overflow: hidden;
106
107 /* Tight under-shadow + wide atmospheric shadow. The hairline ring
108 is the panel's only edge, so it follows the palette; the two
109 atmospheric shadows stay black because they read as depth
110 against the wallpaper, not as part of the surface. */
111 box-shadow:
112 0 0 0 1px var( --os-ui-border, rgba( 0, 0, 0, 0.07 ) ),
113 0 4px 16px rgba( 0, 0, 0, 0.12 ),
114 0 24px 64px rgba( 0, 0, 0, 0.22 );
115
116 /* Spring-physics entrance: starts slightly small + shifted up,
117 overshoots minimally, then settles at 1.0. */
118 transform: scale( 0.96 ) translateY( -8px );
119 transition: transform 0.28s cubic-bezier( 0.34, 1.56, 0.64, 1 );
120 will-change: transform;
121 }
122
123 .os-ai.is-open .os-ai__panel {
124 transform: scale( 1 ) translateY( 0 );
125 }
126
127 /* ---------------------------------------------------------------------------
128 * Header — accent-tinted identity strip
129 * ------------------------------------------------------------------------- */
130
131 .os-ai__header {
132 display: flex;
133 align-items: center;
134 gap: 8px;
135 padding: 10px 14px;
136 /* Gradient fades from a very faint accent tint to white — visible
137 on fresh/blue schemes, subtle on light schemes. */
138 background: linear-gradient(
139 to bottom,
140 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, var( --os-ui-surface, #fff ) ),
141 var( --os-ui-surface, #fff )
142 );
143 border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) );
144 }
145
146 .os-ai__header-icon {
147 display: flex;
148 align-items: center;
149 color: var( --wp-admin-theme-color, #2271b1 );
150 /* Glow so the sparkle feels alive on dark or saturated themes */
151 filter: drop-shadow(
152 0 0 5px color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 55%, transparent )
153 );
154 }
155
156 .os-ai__header-label {
157 flex: 1;
158 font-size: 11px;
159 font-weight: 700;
160 letter-spacing: 0.09em;
161 text-transform: uppercase;
162 color: var( --wp-admin-theme-color, #2271b1 );
163 }
164
165 /* Mode switch (Commands ↔ AI) — a small segmented pill in the header.
166 Shown only when AI is available; replaces the `/` shortcut. */
167 .os-ai__modes {
168 display: inline-flex;
169 gap: 2px;
170 padding: 2px;
171 border-radius: 999px;
172 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
173 }
174
175 .os-ai__modes[hidden] {
176 display: none;
177 }
178
179 .os-ai__mode {
180 appearance: none;
181 border: 0;
182 cursor: pointer;
183 padding: 3px 12px;
184 border-radius: 999px;
185 background: transparent;
186 color: var( --os-ui-fg-muted, #50575e );
187 font-size: 11px;
188 font-weight: 600;
189 line-height: 1.6;
190 }
191
192 .os-ai__mode:hover {
193 color: var( --os-ui-fg, #1d2327 );
194 }
195
196 .os-ai__mode.is-active {
197 /* Elevated first: the pill sits ON the header, which is already
198 `--os-ui-surface`, so a theme that distinguishes the two keeps the
199 active segment readable. Falls through to plain surface, which is
200 the default look. */
201 background: var( --os-ui-surface-elevated, var( --os-ui-surface, #fff ) );
202 color: var( --wp-admin-theme-color, #2271b1 );
203 box-shadow: 0 1px 2px rgba( 0, 0, 0, 0.12 );
204 }
205
206 .os-ai__mode:focus-visible {
207 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
208 outline-offset: 1px;
209 }
210
211 /* .os-ai__close styles moved to the bottom results section */
212
213 /* ---------------------------------------------------------------------------
214 * Input row — the main interaction surface
215 * ------------------------------------------------------------------------- */
216
217 .os-ai__input-wrap {
218 display: flex;
219 align-items: center;
220 gap: 10px;
221 padding: 14px 16px;
222 /* Thin highlight at the bottom of the input area, visible when focused */
223 border-bottom: 1px solid transparent;
224 transition: border-color 0.15s ease;
225 }
226
227 .os-ai__input-wrap:has( .os-ai__input:focus ) {
228 border-bottom-color: var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) );
229 }
230
231 /* Input-row sparkle — visible always, tints toward the accent on focus
232 so the panel reads as "AI assistant" rather than "search box". */
233 .os-ai__input-icon {
234 flex-shrink: 0;
235 display: flex;
236 align-items: center;
237 color: var( --wp-admin-theme-color, #2271b1 );
238 opacity: 0.55;
239 transition: opacity 0.18s ease, transform 0.18s ease;
240 }
241
242 .os-ai__input-wrap:has( .os-ai__input:focus ) .os-ai__input-icon {
243 opacity: 1;
244 transform: scale( 1.08 );
245 }
246
247 /*
248 * The search field is a raw `<input type="text">` in the parent shell,
249 * so core's `forms.css` reaches it:
250 *
251 * input[type="text"], … { background-color: #fff; color: #2c3338;
252 * border: 1px solid #8c8f94;
253 * border-radius: 4px;
254 * box-shadow: 0 0 0 transparent }
255 * input[type="text"]:focus { border-color: #2271b1;
256 * box-shadow: 0 0 0 1px #2271b1 }
257 *
258 * That weighs (0,1,1) — one type selector plus one attribute selector —
259 * which beats a bare `.os-ai__input` at (0,1,0). The result
260 * was a bordered white box floating inside the panel: core's chrome,
261 * core's text colour, ignoring both the design intent (a chromeless
262 * Spotlight-style field) and any active desktop theme.
263 *
264 * Scoping to the overlay root takes the selector to (0,2,0) and wins.
265 * Same fix as the native-window form-control rule in window-chrome.css.
266 */
267 .os-ai .os-ai__input {
268 flex: 1;
269 min-width: 0;
270 appearance: none;
271 border: none;
272 border-radius: 0;
273 outline: none;
274 box-shadow: none;
275 padding: 0;
276 /* Matches the height core gave the control, so the row keeps its
277 comfortable hit target now that core's box is gone. */
278 min-height: 30px;
279 background: transparent;
280 font: inherit;
281 font-size: 17px;
282 font-weight: 400;
283 line-height: 1.4;
284 color: var( --os-ui-fg, #1d2327 );
285 }
286
287 /* Core paints a focus border + ring on the element itself; the focus
288 affordance here is the input row's bottom hairline instead. */
289 .os-ai .os-ai__input:focus {
290 border-color: transparent;
291 box-shadow: none;
292 outline: none;
293 }
294
295 .os-ai .os-ai__input::placeholder {
296 color: var( --os-ui-fg-faint, #c3c4c7 );
297 font-weight: 400;
298 }
299
300 /* Submit button — hidden until the user types something, then
301 springs in via opacity + scale transition. */
302 .os-ai__submit {
303 flex-shrink: 0;
304 display: flex;
305 align-items: center;
306 justify-content: center;
307 width: 30px;
308 height: 30px;
309 border: none;
310 border-radius: 8px;
311 background: var( --wp-admin-theme-color, #2271b1 );
312 color: var( --os-ui-fg-on-accent, #fff );
313 cursor: pointer;
314 padding: 0;
315 opacity: 0;
316 transform: scale( 0.7 );
317 pointer-events: none;
318 transition:
319 opacity 0.2s ease,
320 transform 0.2s cubic-bezier( 0.34, 1.56, 0.64, 1 );
321 }
322
323 .os-ai__submit.has-value {
324 opacity: 1;
325 transform: scale( 1 );
326 pointer-events: auto;
327 }
328
329 .os-ai__submit:hover {
330 filter: brightness( 1.1 );
331 }
332
333 .os-ai__submit:active {
334 transform: scale( 0.92 );
335 }
336
337 .os-ai__submit:focus-visible {
338 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
339 outline-offset: 2px;
340 }
341
342 /* ---------------------------------------------------------------------------
343 * Footer — hints and keyboard shortcut legend
344 * ------------------------------------------------------------------------- */
345
346 .os-ai__footer {
347 display: flex;
348 align-items: center;
349 justify-content: space-between;
350 gap: 8px;
351 padding: 8px 16px 10px;
352 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.018 ) );
353 border-top: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.05 ) );
354 }
355
356 .os-ai__footer-hint {
357 font-size: 11px;
358 color: var( --os-ui-fg-muted, #a7aaad );
359 }
360
361 /* ---------------------------------------------------------------------------
362 * Admin bar AI button — ⌘K badge + sparkle icon
363 * Styles applied via wp_add_inline_style( 'admin-bar', ... ) in PHP.
364 * The styles here are for reference; the actual output lives in
365 * includes/admin-bar.php :: openstation_enqueue_toggle_assets().
366 * ------------------------------------------------------------------------- */
367
368 /* (The admin-bar button styles are added inline in PHP to keep them
369 on the admin-bar handle so they load even before os.js.) */
370
371 /* ---------------------------------------------------------------------------
372 * Close button — × icon, top-right of header
373 * ------------------------------------------------------------------------- */
374
375 .os-ai__close {
376 display: inline-flex;
377 align-items: center;
378 justify-content: center;
379 width: 28px;
380 height: 28px;
381 appearance: none;
382 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
383 border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.1 ) );
384 border-radius: 6px;
385 cursor: pointer;
386 color: var( --os-ui-fg-muted, #646970 );
387 padding: 0;
388 flex-shrink: 0;
389 transition: background 0.1s, color 0.1s, border-color 0.1s;
390 }
391
392 .os-ai__close:hover {
393 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.09 ) );
394 border-color: var( --os-ui-border-strong, rgba( 0, 0, 0, 0.2 ) );
395 color: var( --os-ui-fg, #1d2327 );
396 }
397
398 .os-ai__close:focus-visible {
399 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
400 outline-offset: 2px;
401 }
402
403 /* ---------------------------------------------------------------------------
404 * Results / conversation area — three render modes:
405 * • Suggestions (empty state, example prompts)
406 * • Thinking (spinner + "Thinking…")
407 * • Response (assistant bubble + entity card | admin links | nothing)
408 * ------------------------------------------------------------------------- */
409
410 .os-ai__results {
411 border-top: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) );
412 max-height: 420px;
413 overflow-y: auto;
414 overflow-x: hidden;
415 padding: 14px 16px;
416 display: flex;
417 flex-direction: column;
418 gap: 12px;
419 }
420
421 /* -----------------------------------------------------------------
422 * Suggestion chips (empty state)
423 * ---------------------------------------------------------------- */
424
425 .os-ai__suggestions-label {
426 margin: 0 0 4px;
427 font-size: 11px;
428 font-weight: 600;
429 text-transform: uppercase;
430 letter-spacing: 0.08em;
431 color: var( --os-ui-fg-muted, #8c8f94 );
432 }
433
434 .os-ai__suggestions-list {
435 display: flex;
436 flex-wrap: wrap;
437 gap: 6px;
438 }
439
440 .os-ai__suggestion {
441 appearance: none;
442 border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.1 ) );
443 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
444 border-radius: 999px;
445 padding: 5px 12px;
446 font: inherit;
447 font-size: 12px;
448 color: var( --os-ui-fg-muted, #50575e );
449 cursor: pointer;
450 transition: background 0.12s, border-color 0.12s, color 0.12s;
451 }
452
453 .os-ai__suggestion:hover {
454 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, var( --os-ui-surface, #fff ) );
455 border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 30%, var( --os-ui-border, rgba( 0, 0, 0, 0.1 ) ) );
456 color: var( --wp-admin-theme-color, #2271b1 );
457 }
458
459 /* -----------------------------------------------------------------
460 * Thinking / error state
461 * ---------------------------------------------------------------- */
462
463 .os-ai__state {
464 display: flex;
465 align-items: center;
466 gap: 10px;
467 font-size: 13px;
468 color: var( --os-ui-fg-muted, #646970 );
469 }
470
471 .os-ai__state--error {
472 color: var( --os-ui-danger, #d63638 );
473 align-items: flex-start;
474 }
475
476 /* Inline "AI Settings" link inside the "AI not enabled" error — opens
477 * OS Settings on the AI tab. Styled as a link within the surrounding
478 * error text, not a standalone button: inherits the error color and
479 * flows with the sentence. */
480 .os-ai__settings-link {
481 appearance: none;
482 border: 0;
483 margin: 0;
484 padding: 0;
485 background: none;
486 font: inherit;
487 font-weight: 600;
488 color: inherit;
489 text-decoration: underline;
490 text-underline-offset: 2px;
491 }
492
493 .os-ai__settings-link:hover {
494 text-decoration: none;
495 }
496
497 /* The shell forces `cursor: default !important` on every clickable
498 * surface (see the cursor policy at the top of desktop.css). This
499 * recovery link is an explicit, user-approved exception: it keeps a
500 * pointer so it reads as the actionable link it is. Scoped under
501 * `body.os-active` so it out-specifies the policy's
502 * `!important` rule, same trick the resize-handle exceptions use. */
503 body.os-active .os-ai__settings-link {
504 cursor: pointer !important;
505 }
506
507 @keyframes os-spin {
508 to { transform: rotate( 360deg ); }
509 }
510
511 .os-ai__spinner-icon {
512 animation: os-spin 0.9s linear infinite;
513 flex-shrink: 0;
514 color: var( --wp-admin-theme-color, #2271b1 );
515 }
516
517 /* -----------------------------------------------------------------
518 * Assistant message bubble — prefix for every response
519 * ---------------------------------------------------------------- */
520
521 .os-ai__bubble {
522 display: flex;
523 align-items: flex-start;
524 gap: 10px;
525 }
526
527 .os-ai__bubble-icon {
528 flex-shrink: 0;
529 display: flex;
530 align-items: center;
531 justify-content: center;
532 width: 24px;
533 height: 24px;
534 border-radius: 50%;
535 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 12%, var( --os-ui-surface, #fff ) );
536 color: var( --wp-admin-theme-color, #2271b1 );
537 }
538
539 .os-ai__bubble-text {
540 padding-top: 3px;
541 font-size: 13px;
542 line-height: 1.5;
543 color: var( --os-ui-fg, #1d2327 );
544 flex: 1;
545 min-width: 0;
546 }
547
548 /* Markdown output inside the assistant bubble — the renderer emits
549 <p>, <ul>/<ol>/<li>, <strong>, <em>, <code>, <a>. Reset margins on
550 outer paragraphs so single-paragraph replies (the common case)
551 don't get top/bottom spacing; list items get the usual disc/decimal
552 markers inside a compact indent. */
553 .os-ai__bubble-text > p,
554 .os-ai__bubble-text > ul,
555 .os-ai__bubble-text > ol {
556 margin: 0 0 6px;
557 }
558 .os-ai__bubble-text > :last-child {
559 margin-bottom: 0;
560 }
561 .os-ai__bubble-text ul,
562 .os-ai__bubble-text ol {
563 padding-inline-start: 20px;
564 }
565 .os-ai__bubble-text li {
566 margin-bottom: 2px;
567 }
568 .os-ai__bubble-text code {
569 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.05 ) );
570 padding: 1px 5px;
571 border-radius: 4px;
572 font-family: var( --os-ui-font-mono, Menlo, Consolas, monospace );
573 font-size: 11.5px;
574 }
575 .os-ai__bubble-text a {
576 color: var( --wp-admin-theme-color, #2271b1 );
577 text-decoration: underline;
578 text-underline-offset: 2px;
579 }
580 .os-ai__bubble-text a:hover {
581 text-decoration: none;
582 }
583 .os-ai__bubble-text strong {
584 font-weight: 600;
585 color: var( --os-ui-fg, #1d2327 );
586 }
587
588 /* -----------------------------------------------------------------
589 * Entity card — shown under the bubble when answer_type=entity
590 * ---------------------------------------------------------------- */
591
592 .os-ai__entity {
593 display: flex;
594 flex-direction: column;
595 gap: 8px;
596 padding: 12px 14px;
597 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
598 border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) );
599 border-radius: 10px;
600 }
601
602 .os-ai__entity-header {
603 display: flex;
604 align-items: center;
605 gap: 8px;
606 flex-wrap: wrap;
607 }
608
609 .os-ai__entity-topic {
610 display: inline-block;
611 font-size: 10px;
612 font-weight: 600;
613 text-transform: uppercase;
614 letter-spacing: 0.07em;
615 color: var( --wp-admin-theme-color, #2271b1 );
616 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, transparent );
617 padding: 2px 7px;
618 border-radius: 999px;
619 }
620
621 .os-ai__entity-type {
622 font-size: 10px;
623 font-weight: 600;
624 text-transform: uppercase;
625 letter-spacing: 0.07em;
626 color: var( --os-ui-fg-muted, #8c8f94 );
627 }
628
629 .os-ai__entity-title {
630 margin: 0;
631 font-size: 14px;
632 font-weight: 600;
633 color: var( --os-ui-fg, #1d2327 );
634 line-height: 1.35;
635 }
636
637 .os-ai__entity-summary {
638 margin: 0;
639 font-size: 12px;
640 line-height: 1.5;
641 color: var( --os-ui-fg-muted, #50575e );
642 }
643
644 /* Primary action — opens the entity in a legacy iframe window inside
645 the desktop (wp-admin edit URL). */
646 .os-ai__entity-open {
647 appearance: none;
648 display: inline-flex;
649 align-items: center;
650 gap: 6px;
651 align-self: flex-start;
652 padding: 6px 14px;
653 border: none;
654 border-radius: 8px;
655 background: var( --wp-admin-theme-color, #2271b1 );
656 color: var( --os-ui-fg-on-accent, #fff );
657 font: inherit;
658 font-size: 12px;
659 font-weight: 500;
660 cursor: pointer;
661 transition: filter 0.1s, transform 0.1s;
662 }
663
664 .os-ai__entity-open:hover {
665 filter: brightness( 1.1 );
666 }
667
668 .os-ai__entity-open:active {
669 transform: scale( 0.97 );
670 }
671
672 .os-ai__entity-open:focus-visible {
673 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
674 outline-offset: 2px;
675 }
676
677 .os-ai__entity-open svg {
678 margin-inline-start: 2px;
679 }
680
681 /* -----------------------------------------------------------------
682 * Admin-links list — shown when answer_type=navigation
683 * ---------------------------------------------------------------- */
684
685 .os-ai__admin-links {
686 display: flex;
687 flex-direction: column;
688 gap: 6px;
689 }
690
691 .os-ai__admin-link {
692 appearance: none;
693 display: flex;
694 align-items: center;
695 gap: 12px;
696 padding: 10px 12px;
697 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.02 ) );
698 border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) );
699 border-radius: 10px;
700 cursor: pointer;
701 text-align: start;
702 font: inherit;
703 color: inherit;
704 transition: background 0.12s, border-color 0.12s, transform 0.08s;
705 }
706
707 .os-ai__admin-link:hover {
708 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 6%, var( --os-ui-surface, #fff ) );
709 border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ) );
710 }
711
712 .os-ai__admin-link:active {
713 transform: scale( 0.99 );
714 }
715
716 .os-ai__admin-link:focus-visible {
717 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
718 outline-offset: 2px;
719 }
720
721 .os-ai__admin-link-icon {
722 flex-shrink: 0;
723 display: flex;
724 align-items: center;
725 justify-content: center;
726 width: 32px;
727 height: 32px;
728 font-size: 18px;
729 color: var( --wp-admin-theme-color, #2271b1 );
730 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, var( --os-ui-surface, #fff ) );
731 border-radius: 8px;
732 }
733
734 .os-ai__admin-link-body {
735 flex: 1;
736 display: flex;
737 flex-direction: column;
738 gap: 2px;
739 min-width: 0;
740 }
741
742 .os-ai__admin-link-title {
743 font-size: 13px;
744 font-weight: 600;
745 color: var( --os-ui-fg, #1d2327 );
746 }
747
748 .os-ai__admin-link-desc {
749 font-size: 11px;
750 color: var( --os-ui-fg-muted, #646970 );
751 line-height: 1.4;
752 overflow: hidden;
753 text-overflow: ellipsis;
754 white-space: nowrap;
755 }
756
757 .os-ai__admin-link-arrow {
758 flex-shrink: 0;
759 color: var( --os-ui-fg-faint, #c3c4c7 );
760 transition: color 0.12s, transform 0.12s;
761 }
762
763 .os-ai__admin-link:hover .os-ai__admin-link-arrow {
764 color: var( --wp-admin-theme-color, #2271b1 );
765 transform: translateX( 2px );
766 }
767
768 /* -----------------------------------------------------------------
769 * Command palette — shown when the user types "/" in the input.
770 * Filtered list of plugin-registered slash-commands. Mirrors the
771 * admin-link list's visual shape (icon + title + description) but
772 * uses a dedicated set of classes so each can evolve independently.
773 * ---------------------------------------------------------------- */
774
775 .os-ai__cmd-list {
776 display: flex;
777 flex-direction: column;
778 gap: 4px;
779 }
780
781 .os-ai__cmd-item {
782 appearance: none;
783 display: flex;
784 align-items: flex-start;
785 gap: 10px;
786 padding: 8px 10px;
787 background: transparent;
788 border: 1px solid transparent;
789 border-radius: 8px;
790 cursor: pointer;
791 text-align: start;
792 font: inherit;
793 color: inherit;
794 transition: background 0.1s, border-color 0.1s;
795 }
796
797 /*
798 * Single-line rows (no `.os-ai__cmd-desc` child) need the
799 * icon + label baseline-centered instead of top-aligned, otherwise
800 * the label floats to the top of the taller icon tile and reads as
801 * misaligned. `:has()` is widely supported in evergreen browsers the
802 * desktop shell already targets.
803 */
804 .os-ai__cmd-item:not(:has(.os-ai__cmd-desc)) {
805 align-items: center;
806 }
807
808 .os-ai__cmd-item:hover,
809 .os-ai__cmd-item.is-selected {
810 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, var( --os-ui-surface, #fff ) );
811 border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ) );
812 }
813
814 /*
815 * When keyboard navigation is driving selection, suppress the :hover
816 * style on rows. Without this, arrowing the cursor past the row that
817 * happens to sit under the mouse pointer paints BOTH rows as active
818 * — the keyboard target from `.is-selected` and the pointer target
819 * from `:hover`. The JS toggles this class on the list container on
820 * ArrowUp/Down and clears it on the next real `mousemove`.
821 */
822 .os-ai__cmd-list--kb-nav .os-ai__cmd-item:hover {
823 background: transparent;
824 border-color: transparent;
825 }
826 .os-ai__cmd-list--kb-nav .os-ai__cmd-item.is-selected:hover {
827 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, var( --os-ui-surface, #fff ) );
828 border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ) );
829 }
830
831 .os-ai__cmd-item:focus-visible {
832 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
833 outline-offset: 2px;
834 }
835
836 .os-ai__cmd-icon {
837 flex-shrink: 0;
838 display: flex;
839 align-items: center;
840 justify-content: center;
841 width: 36px;
842 height: 36px;
843 font-size: 20px;
844 color: var( --wp-admin-theme-color, #2271b1 );
845 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, var( --os-ui-surface, #fff ) );
846 border-radius: 7px;
847 }
848
849 /*
850 * Dashicons ship at a 20px baseline; the tile was scaled up but
851 * dashicons glyphs need explicit sizing to follow.
852 */
853 .os-ai__cmd-icon.dashicons {
854 font-size: 22px;
855 width: 36px;
856 height: 36px;
857 line-height: 36px;
858 }
859
860 /*
861 * Raw-SVG icon slot (command bridge forwards rendered
862 * `@wordpress/icons` markup). Constrain the inline SVG so it sits
863 * centered inside the tile regardless of the viewBox the source icon
864 * shipped with.
865 */
866 .os-ai__cmd-icon--svg svg {
867 width: 24px;
868 height: 24px;
869 fill: currentColor;
870 }
871
872 .os-ai__cmd-body {
873 flex: 1;
874 display: flex;
875 flex-direction: column;
876 gap: 2px;
877 min-width: 0;
878 }
879
880 .os-ai__cmd-title {
881 font-size: 13px;
882 font-weight: 600;
883 color: var( --os-ui-fg, #1d2327 );
884 font-family: var( --os-ui-font-mono, Menlo, Consolas, monospace );
885 letter-spacing: -0.01em;
886 }
887
888 .os-ai__cmd-hint {
889 font-weight: 400;
890 color: var( --os-ui-fg-muted, #8c8f94 );
891 font-size: 12px;
892 margin-inline-start: 6px;
893 }
894
895 .os-ai__cmd-desc {
896 font-size: 11.5px;
897 color: var( --os-ui-fg-muted, #646970 );
898 line-height: 1.4;
899 font-family: inherit;
900 letter-spacing: 0;
901 }
902
903 /* Entity search results (posts, pages) — rendered in the same list as
904 commands but are navigable destinations, not actions. Use regular font
905 (not the monospace used for slash-commands) so they read as content
906 search results, matching the classic command palette UX. */
907 .os-ai__cmd-item.is-entity-result .os-ai__cmd-title {
908 font-family: inherit;
909 letter-spacing: 0;
910 }
911
912 /* Args mode — a single row showing the locked-in command. Same
913 visual language as the list item but with an "Enter to run" hint. */
914 .os-ai__cmd-active {
915 display: flex;
916 align-items: flex-start;
917 gap: 10px;
918 padding: 10px 12px;
919 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 10%, var( --os-ui-surface, #fff ) );
920 border: 1px solid color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 30%, var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ) );
921 border-radius: 8px;
922 }
923
924 .os-ai__cmd-enter-hint {
925 font-size: 11px;
926 color: var( --os-ui-fg-muted, #646970 );
927 margin-top: 2px;
928 font-family: inherit;
929 }
930
931 .os-ai__cmd-enter-hint kbd {
932 font-family: inherit;
933 font-size: 10px;
934 padding: 1px 4px;
935 border: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.12 ) );
936 border-radius: 3px;
937 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.03 ) );
938 color: var( --os-ui-fg-muted, #646970 );
939 }
940
941 /* Suggestion list shown under the locked-in command in args mode.
942 Visually identical to the command picker rows but without the
943 border-highlight — the "active" glow already lives on the command
944 header directly above. */
945 .os-ai__cmd-suggest-list {
946 display: flex;
947 flex-direction: column;
948 gap: 2px;
949 margin-top: 6px;
950 }
951
952 .os-ai__cmd-suggest-item {
953 appearance: none;
954 display: flex;
955 align-items: flex-start;
956 gap: 10px;
957 padding: 6px 10px;
958 background: transparent;
959 border: 1px solid transparent;
960 border-radius: 6px;
961 cursor: pointer;
962 text-align: start;
963 font: inherit;
964 color: inherit;
965 transition: background 0.1s, border-color 0.1s;
966 }
967
968 .os-ai__cmd-suggest-item:hover,
969 .os-ai__cmd-suggest-item.is-selected {
970 background: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 8%, var( --os-ui-surface, #fff ) );
971 border-color: color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 25%, var( --os-ui-border, rgba( 0, 0, 0, 0.08 ) ) );
972 }
973
974 .os-ai__cmd-suggest-label {
975 font-size: 13px;
976 font-weight: 500;
977 color: var( --os-ui-fg, #1d2327 );
978 }
979
980 /* Empty / "no matches" state for the palette */
981 .os-ai__state--empty {
982 color: var( --os-ui-fg-muted, #8c8f94 );
983 font-size: 13px;
984 }
985
986 /* -----------------------------------------------------------------
987 * Continue button (budget-exhausted state)
988 * ---------------------------------------------------------------- */
989
990 .os-ai__continue-btn {
991 appearance: none;
992 display: inline-flex;
993 align-items: center;
994 align-self: flex-start;
995 gap: 6px;
996 font: inherit;
997 font-size: 12px;
998 font-weight: 500;
999 padding: 6px 12px;
1000 border: 1px dashed var( --os-ui-border-strong, rgba( 0, 0, 0, 0.2 ) );
1001 border-radius: 6px;
1002 background: transparent;
1003 color: var( --os-ui-fg-muted, #50575e );
1004 cursor: pointer;
1005 transition: background 0.1s, border-color 0.1s, color 0.1s;
1006 }
1007
1008 .os-ai__continue-btn:hover {
1009 background: var( --os-ui-hover, rgba( 0, 0, 0, 0.04 ) );
1010 border-color: var( --os-ui-border-strong, rgba( 0, 0, 0, 0.3 ) );
1011 color: var( --os-ui-fg, #1d2327 );
1012 }
1013