@1agh/maude 0.45.1 → 0.45.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/apps/studio/acp/bridge.ts +585 -73
  2. package/apps/studio/acp/index.ts +172 -23
  3. package/apps/studio/acp/probe.ts +31 -7
  4. package/apps/studio/acp/transcript.ts +91 -5
  5. package/apps/studio/bin/_import-asset.mjs +35 -5
  6. package/apps/studio/client/panels/CapabilityBar.jsx +101 -0
  7. package/apps/studio/client/panels/ChatPanel.jsx +802 -133
  8. package/apps/studio/client/panels/ElicitationPrompt.jsx +429 -0
  9. package/apps/studio/client/panels/PermissionPrompt.jsx +119 -0
  10. package/apps/studio/client/panels/ReadinessList.jsx +17 -3
  11. package/apps/studio/client/panels/ToolGroup.jsx +79 -0
  12. package/apps/studio/client/panels/acp-capabilities.js +71 -0
  13. package/apps/studio/client/panels/acp-elicitation.js +194 -0
  14. package/apps/studio/client/panels/acp-runtime.js +248 -9
  15. package/apps/studio/client/panels/acp-usage.js +65 -0
  16. package/apps/studio/client/panels/transcript-view.js +36 -0
  17. package/apps/studio/client/styles/6-acp-chat.css +648 -11
  18. package/apps/studio/dist/client.bundle.js +1596 -1594
  19. package/apps/studio/dist/styles.css +1 -1
  20. package/apps/studio/generation/whisper-models.test.ts +28 -1
  21. package/apps/studio/generation/whisper-models.ts +22 -7
  22. package/apps/studio/http.ts +33 -2
  23. package/apps/studio/test/acp-bridge.test.ts +11 -6
  24. package/apps/studio/test/acp-capabilities.test.ts +123 -0
  25. package/apps/studio/test/acp-caps-bridge.test.ts +274 -0
  26. package/apps/studio/test/acp-elicitation-bridge.test.ts +475 -0
  27. package/apps/studio/test/acp-elicitation.test.ts +251 -0
  28. package/apps/studio/test/acp-permission-prompt.test.ts +77 -0
  29. package/apps/studio/test/acp-permission.test.ts +262 -0
  30. package/apps/studio/test/acp-toolgroup.test.ts +76 -0
  31. package/apps/studio/test/acp-transcript-view.test.ts +71 -0
  32. package/apps/studio/test/acp-transcript.test.ts +75 -2
  33. package/apps/studio/test/acp-usage-bridge.test.ts +136 -0
  34. package/apps/studio/test/acp-usage.test.ts +143 -0
  35. package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +158 -0
  36. package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +46 -0
  37. package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +42 -0
  38. package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +63 -0
  39. package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +51 -0
  40. package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +50 -0
  41. package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +56 -0
  42. package/apps/studio/test/import-asset.test.ts +31 -3
  43. package/apps/studio/whats-new.json +34 -0
  44. package/cli/commands/design.mjs +107 -2
  45. package/cli/lib/pkg-root.mjs +42 -10
  46. package/cli/lib/pkg-root.test.mjs +33 -1
  47. package/package.json +11 -9
@@ -136,6 +136,7 @@
136
136
  padding: var(--space-1);
137
137
  }
138
138
  .chat-menu-row {
139
+ position: relative;
139
140
  display: flex;
140
141
  align-items: center;
141
142
  border-radius: var(--radius-xs);
@@ -167,18 +168,78 @@
167
168
  text-overflow: ellipsis;
168
169
  white-space: nowrap;
169
170
  }
170
- .chat-menu-del {
171
+ /* Inline rename input (Task C5) — same box model as .chat-menu-open so the
172
+ row doesn't jump when switching between the button and the input. */
173
+ .chat-menu-rename-input {
174
+ flex: 1;
175
+ min-width: 0;
176
+ appearance: none;
177
+ border: 1px solid var(--accent);
178
+ border-radius: var(--radius-xs);
179
+ background: var(--bg-1);
180
+ color: var(--fg-0);
181
+ font-family: var(--font-body);
182
+ font-size: var(--type-xs);
183
+ padding: var(--space-2);
184
+ margin: 2px;
185
+ outline: none;
186
+ }
187
+
188
+ /* Per-chat overflow (⋯ → Rename / Archive / Copy transcript / Delete). */
189
+ .chat-menu-overflow-wrap {
190
+ position: relative;
191
+ flex-shrink: 0;
192
+ }
193
+ .chat-menu-more {
171
194
  appearance: none;
172
195
  border: 0;
173
196
  background: transparent;
174
197
  cursor: pointer;
175
198
  color: var(--fg-3);
176
199
  display: inline-flex;
177
- padding: var(--space-2);
200
+ align-items: center;
201
+ justify-content: center;
202
+ width: 22px;
203
+ height: 22px;
204
+ padding: 0;
178
205
  border-radius: var(--radius-xs);
179
206
  flex-shrink: 0;
207
+ font-size: var(--type-sm);
208
+ }
209
+ .chat-menu-more:hover {
210
+ background: var(--bg-4, var(--bg-3));
211
+ color: var(--fg-0);
212
+ }
213
+ .chat-menu-overflow {
214
+ position: absolute;
215
+ top: calc(100% + 2px);
216
+ right: 0;
217
+ z-index: 52;
218
+ display: flex;
219
+ flex-direction: column;
220
+ min-width: 150px;
221
+ padding: var(--space-1);
222
+ background: var(--bg-2);
223
+ border: 1px solid var(--border-default);
224
+ border-radius: var(--radius-sm);
225
+ box-shadow: var(--shadow-lg);
226
+ }
227
+ .chat-menu-overflow button {
228
+ appearance: none;
229
+ border: 0;
230
+ background: transparent;
231
+ text-align: left;
232
+ cursor: pointer;
233
+ color: var(--fg-1);
234
+ font-family: var(--font-body);
235
+ font-size: var(--type-xs);
236
+ padding: var(--space-2);
237
+ border-radius: var(--radius-xs);
180
238
  }
181
- .chat-menu-del:hover {
239
+ .chat-menu-overflow button:hover {
240
+ background: var(--bg-3);
241
+ }
242
+ .chat-menu-overflow-danger {
182
243
  color: var(--status-error);
183
244
  }
184
245
  .chat-dot {
@@ -197,6 +258,20 @@
197
258
  .chat-dot--off {
198
259
  background: var(--border-strong);
199
260
  }
261
+ /* Transcript view label (Task C4/C5) — a bare unlabeled select read as
262
+ unexplained UI in dogfooding; this small caption fixes that. */
263
+ .chat-view-label {
264
+ display: flex;
265
+ align-items: center;
266
+ gap: 4px;
267
+ flex-shrink: 0;
268
+ }
269
+ .chat-view-label-tx {
270
+ font-family: var(--font-mono);
271
+ font-size: var(--type-xs);
272
+ color: var(--fg-3);
273
+ white-space: nowrap;
274
+ }
200
275
  .chat-newbtn {
201
276
  appearance: none;
202
277
  border: 1px solid var(--border-default);
@@ -239,6 +314,9 @@
239
314
  .chat-statusrow-cc {
240
315
  color: var(--fg-1);
241
316
  }
317
+ .chat-statusrow-spacer {
318
+ flex: 1;
319
+ }
242
320
  .chat-status-dot {
243
321
  width: 7px;
244
322
  height: 7px;
@@ -398,6 +476,7 @@
398
476
 
399
477
  /* Messages */
400
478
  .chat-msg {
479
+ position: relative;
401
480
  display: flex;
402
481
  flex-direction: column;
403
482
  gap: var(--space-2);
@@ -457,6 +536,58 @@
457
536
  padding-top: var(--space-3);
458
537
  }
459
538
 
539
+ /* Per-message hover actions (Task C2 — Copy / Retry). Absolutely positioned
540
+ so it never reflows the bubble; hover- AND focus-revealed (keyboard users
541
+ tab to the buttons, which triggers :focus-within even without a pointer). */
542
+ .chat-msg-actions {
543
+ position: absolute;
544
+ top: calc(-1 * var(--space-2));
545
+ right: 0;
546
+ display: flex;
547
+ gap: 2px;
548
+ padding: 2px;
549
+ border: 1px solid var(--border-subtle);
550
+ border-radius: var(--radius-sm);
551
+ background: var(--bg-2);
552
+ opacity: 0;
553
+ pointer-events: none;
554
+ transition: opacity var(--dur-fast, 120ms) ease;
555
+ }
556
+ .chat-msg:hover .chat-msg-actions,
557
+ .chat-msg:focus-within .chat-msg-actions {
558
+ opacity: 1;
559
+ pointer-events: auto;
560
+ }
561
+ .chat-msg-action {
562
+ appearance: none;
563
+ border: 0;
564
+ background: transparent;
565
+ color: var(--fg-2);
566
+ display: inline-flex;
567
+ align-items: center;
568
+ justify-content: center;
569
+ width: 22px;
570
+ height: 22px;
571
+ border-radius: var(--radius-xs);
572
+ cursor: pointer;
573
+ }
574
+ .chat-msg-action:hover {
575
+ background: var(--bg-3);
576
+ color: var(--fg-0);
577
+ }
578
+ .chat-msg-action:disabled {
579
+ opacity: 0.4;
580
+ cursor: default;
581
+ }
582
+ .chat-msg-action[data-copied='true'] {
583
+ color: var(--status-success);
584
+ }
585
+ @media (prefers-reduced-motion: reduce) {
586
+ .chat-msg-actions {
587
+ transition: none;
588
+ }
589
+ }
590
+
460
591
  /* Rendered markdown inside the assistant bubble (chat-markdown.jsx) */
461
592
  .chat-msg--assistant .chat-bubble > * + * {
462
593
  margin-top: var(--space-2);
@@ -553,7 +684,79 @@
553
684
  white-space: pre-wrap;
554
685
  }
555
686
 
556
- /* Tool-call card */
687
+ /* Collapsed consecutive-tool-call group (Task C1) — mirrors .chat-think's
688
+ <details>/<summary> disclosure pattern exactly. */
689
+ .chat-toolgroup {
690
+ border: 1px solid var(--border-default);
691
+ border-radius: var(--radius-sm);
692
+ background: var(--bg-2);
693
+ overflow: hidden;
694
+ }
695
+ .chat-toolgroup-sum {
696
+ display: flex;
697
+ align-items: center;
698
+ gap: var(--space-2);
699
+ padding: var(--space-2) var(--space-3);
700
+ font-family: var(--font-mono);
701
+ font-size: var(--type-xs);
702
+ color: var(--fg-1);
703
+ cursor: pointer;
704
+ user-select: none;
705
+ list-style: none;
706
+ }
707
+ .chat-toolgroup-sum::-webkit-details-marker {
708
+ display: none;
709
+ }
710
+ .chat-toolgroup-sum::after {
711
+ content: '▸';
712
+ margin-left: auto;
713
+ color: var(--fg-3);
714
+ transition: transform var(--dur-soft) var(--ease-out);
715
+ }
716
+ .chat-toolgroup[open] .chat-toolgroup-sum::after {
717
+ transform: rotate(90deg);
718
+ }
719
+ .chat-toolgroup[open] .chat-toolgroup-sum {
720
+ border-bottom: 1px solid var(--border-subtle);
721
+ }
722
+ .chat-toolgroup-body {
723
+ display: flex;
724
+ flex-direction: column;
725
+ }
726
+ /* Flat accordion row (Task C1 revision) — a bordered card-in-a-card per
727
+ tool read as "divné" (weird) in dogfooding; a group's individual tool
728
+ calls are now plain rows separated by a hairline, not their own
729
+ `.chat-tool` card, so the whole group stays one lightweight shape. */
730
+ .chat-tool-row {
731
+ padding: var(--space-2) var(--space-3);
732
+ display: flex;
733
+ flex-direction: column;
734
+ gap: var(--space-1);
735
+ }
736
+ .chat-tool-row + .chat-tool-row {
737
+ border-top: 1px solid var(--border-subtle);
738
+ }
739
+ .chat-tool-row-main {
740
+ display: flex;
741
+ align-items: center;
742
+ gap: var(--space-2);
743
+ font-family: var(--font-mono);
744
+ font-size: var(--type-xs);
745
+ color: var(--fg-1);
746
+ }
747
+ .chat-tool-row-main b {
748
+ color: var(--accent);
749
+ font-weight: 600;
750
+ }
751
+ .chat-tool-row-status {
752
+ margin-left: auto;
753
+ color: var(--fg-3);
754
+ }
755
+ .chat-tool-row-status.del {
756
+ color: var(--status-error);
757
+ }
758
+
759
+ /* Tool-call card (standalone — a lone tool call outside a group). */
557
760
  .chat-tool {
558
761
  border: 1px solid var(--border-default);
559
762
  border-radius: var(--radius-sm);
@@ -608,6 +811,340 @@
608
811
  .chat-tool-line.del {
609
812
  color: var(--status-error);
610
813
  }
814
+ /* Raw args/result preview — Verbose transcript view only (Task C4). */
815
+ .chat-tool-detail {
816
+ margin: 0;
817
+ padding: var(--space-2);
818
+ background: var(--bg-3);
819
+ border: 1px solid var(--border-subtle);
820
+ border-radius: var(--radius-xs);
821
+ color: var(--fg-2);
822
+ font-family: var(--font-mono);
823
+ font-size: var(--type-xs);
824
+ white-space: pre-wrap;
825
+ overflow-wrap: anywhere;
826
+ }
827
+
828
+ /* Permission approve/deny card (Milestone B — retires DDR-125 F2's blanket
829
+ auto-approve). Sits between the feed and the composer, one at a time. */
830
+ .chat-perm {
831
+ display: flex;
832
+ flex-direction: column;
833
+ gap: var(--space-2);
834
+ margin: 0 var(--space-4);
835
+ padding: var(--space-3);
836
+ border: 1px solid var(--accent);
837
+ border-radius: var(--radius-sm);
838
+ background: var(--bg-2);
839
+ }
840
+ .chat-perm:focus-visible {
841
+ outline: none;
842
+ }
843
+ .chat-perm-hd {
844
+ display: flex;
845
+ align-items: center;
846
+ gap: var(--space-2);
847
+ font-family: var(--font-mono);
848
+ font-size: var(--type-xs);
849
+ color: var(--fg-1);
850
+ }
851
+ /* SECURITY (ethical-hacker finding) — the title is adapter/model-controlled
852
+ (for Bash, it's the raw shell command) with no length bound; without a
853
+ clamp a crafted/long command could push its dangerous tail off-screen in
854
+ a card whose entire point is "you can see what you're approving". Clamps
855
+ to one legible line; the full string is still reachable via the native
856
+ title-attribute tooltip (see PermissionPrompt.jsx). */
857
+ .chat-perm-hd b {
858
+ display: block;
859
+ min-width: 0;
860
+ flex: 1;
861
+ overflow: hidden;
862
+ text-overflow: ellipsis;
863
+ white-space: nowrap;
864
+ color: var(--accent);
865
+ font-weight: 600;
866
+ }
867
+ .chat-perm-queue {
868
+ flex-shrink: 0;
869
+ color: var(--fg-3);
870
+ font-weight: 400;
871
+ }
872
+ .chat-perm-body {
873
+ margin: 0;
874
+ font-size: var(--type-xs);
875
+ color: var(--fg-2);
876
+ }
877
+ .chat-perm-actions {
878
+ display: flex;
879
+ flex-wrap: wrap;
880
+ gap: var(--space-2);
881
+ }
882
+
883
+ /* Form-elicitation card (feature-acp-ask-user-question — AskUserQuestion +
884
+ generic MCP form input). Same slot + shape as .chat-perm, mutually
885
+ exclusive with it — see the mount site in ChatPanel.jsx. */
886
+ .chat-elicit {
887
+ display: flex;
888
+ flex-direction: column;
889
+ gap: var(--space-3);
890
+ margin: 0 var(--space-4);
891
+ padding: var(--space-3) var(--space-4);
892
+ border: 1px solid var(--border-default);
893
+ border-left: 2px solid var(--accent);
894
+ border-radius: var(--radius-sm);
895
+ background: var(--bg-2);
896
+ }
897
+ .chat-elicit:focus-visible {
898
+ outline: none;
899
+ }
900
+ .chat-elicit-hd {
901
+ display: flex;
902
+ flex-direction: column;
903
+ gap: var(--space-1);
904
+ }
905
+ .chat-elicit-hd b {
906
+ color: var(--fg-0);
907
+ font-size: var(--type-sm);
908
+ font-weight: 600;
909
+ line-height: var(--lh-sm);
910
+ }
911
+ .chat-elicit-meta {
912
+ display: flex;
913
+ align-items: center;
914
+ gap: var(--space-2);
915
+ font-family: var(--font-mono);
916
+ font-size: var(--type-xs);
917
+ color: var(--fg-3);
918
+ }
919
+ .chat-elicit-meta > span + span::before {
920
+ content: '·';
921
+ margin-right: var(--space-2);
922
+ }
923
+ .chat-elicit-question {
924
+ display: flex;
925
+ flex-direction: column;
926
+ gap: var(--space-2);
927
+ margin: 0;
928
+ padding: 0;
929
+ border: none;
930
+ }
931
+ .chat-elicit-question-hd {
932
+ display: flex;
933
+ flex-direction: column;
934
+ gap: var(--space-2);
935
+ padding: 0;
936
+ font-size: var(--type-sm);
937
+ color: var(--fg-0);
938
+ line-height: var(--lh-sm);
939
+ }
940
+ .chat-elicit-question-chip {
941
+ align-self: flex-start;
942
+ padding: 2px var(--space-2);
943
+ border-radius: var(--radius-pill);
944
+ background: var(--bg-3);
945
+ color: var(--accent);
946
+ font-family: var(--font-mono);
947
+ font-size: 10px;
948
+ font-weight: 600;
949
+ text-transform: uppercase;
950
+ letter-spacing: 0.04em;
951
+ }
952
+ .chat-elicit-options {
953
+ display: flex;
954
+ flex-direction: column;
955
+ gap: var(--space-1);
956
+ }
957
+ .chat-elicit-option {
958
+ display: flex;
959
+ align-items: flex-start;
960
+ gap: var(--space-2);
961
+ font-size: var(--type-xs);
962
+ color: var(--fg-1);
963
+ cursor: pointer;
964
+ accent-color: var(--accent);
965
+ }
966
+ /* "Other" rides in the radio group itself (single-select only — see
967
+ ElicitationPrompt.jsx) rather than a separate link below it, so the
968
+ either/or truth (one field, one answer) is visible for free through
969
+ native radio-group exclusivity. Styled as a peer option, not a
970
+ secondary action — no visual demotion, it's a real 4th choice. */
971
+ .chat-elicit-option--other {
972
+ color: var(--fg-2);
973
+ }
974
+ .chat-elicit-custom--inline {
975
+ margin-left: calc(16px + var(--space-2)); /* aligns under the "Other" label, not the radio dot */
976
+ }
977
+ .chat-elicit-option-body {
978
+ display: flex;
979
+ flex-direction: column;
980
+ gap: var(--space-1);
981
+ }
982
+ .chat-elicit-option-desc {
983
+ color: var(--fg-2);
984
+ }
985
+ .chat-elicit-option-preview {
986
+ margin: 0;
987
+ padding: var(--space-1) var(--space-2);
988
+ background: var(--bg-3);
989
+ border: 1px solid var(--border-subtle);
990
+ border-radius: var(--radius-xs);
991
+ color: var(--fg-2);
992
+ font-family: var(--font-mono);
993
+ font-size: var(--type-xs);
994
+ white-space: pre-wrap;
995
+ overflow-wrap: anywhere;
996
+ }
997
+ .chat-elicit-text {
998
+ width: 100%;
999
+ padding: var(--space-1) var(--space-2);
1000
+ background: var(--bg-3);
1001
+ border: 1px solid var(--border-subtle);
1002
+ border-radius: var(--radius-xs);
1003
+ color: var(--fg-0);
1004
+ font-size: var(--type-xs);
1005
+ font-family: inherit;
1006
+ }
1007
+ .chat-elicit-custom-toggle {
1008
+ align-self: flex-start;
1009
+ padding: 0;
1010
+ background: none;
1011
+ border: none;
1012
+ color: var(--accent);
1013
+ font-size: var(--type-xs);
1014
+ cursor: pointer;
1015
+ text-decoration: underline;
1016
+ }
1017
+ .chat-elicit-custom-wrap {
1018
+ display: flex;
1019
+ flex-direction: column;
1020
+ gap: var(--space-1);
1021
+ }
1022
+ .chat-elicit-custom-note {
1023
+ margin: 0;
1024
+ color: var(--fg-2);
1025
+ font-size: var(--type-xs);
1026
+ }
1027
+ /* Credential-shaped question warning (ethical-hacker finding — see
1028
+ acp-elicitation.js's looksLikeSecretRequest). Border/left-accent carries
1029
+ the warning color; TEXT stays on --fg-1 rather than --status-warn
1030
+ directly — that token fails AA contrast at this size (bit the a11y
1031
+ review once already, per DDR-097's file-tree badge fix). */
1032
+ .chat-elicit-secret-warning {
1033
+ margin: 0;
1034
+ padding: var(--space-1) var(--space-2);
1035
+ border: 1px solid var(--status-warn);
1036
+ border-radius: var(--radius-xs);
1037
+ background: color-mix(in oklab, var(--status-warn) 12%, transparent);
1038
+ color: var(--fg-1);
1039
+ font-size: var(--type-xs);
1040
+ }
1041
+ /* SECURITY (ethical-hacker finding) — shown when a question reads as a
1042
+ credential ask; pairs with the masked input in ElicitationPrompt.jsx. */
1043
+ .chat-elicit-secret-warning {
1044
+ margin: 0;
1045
+ color: var(--warn, hsl(38 92% 50%));
1046
+ font-size: var(--type-xs);
1047
+ }
1048
+ .chat-elicit-actions {
1049
+ display: flex;
1050
+ flex-wrap: wrap;
1051
+ align-items: center;
1052
+ gap: var(--space-2);
1053
+ }
1054
+ .chat-elicit-actions-spacer {
1055
+ flex: 1 1 auto;
1056
+ }
1057
+
1058
+ /* Connection-problem error card (Task C3) — transient bridge/socket failure,
1059
+ recoverable with a retry. Same slot as .chat-perm (mutually exclusive). */
1060
+ .chat-error-card {
1061
+ display: flex;
1062
+ flex-direction: column;
1063
+ gap: var(--space-2);
1064
+ margin: 0 var(--space-4);
1065
+ padding: var(--space-3);
1066
+ border: 1px solid var(--status-error);
1067
+ border-radius: var(--radius-sm);
1068
+ background: var(--bg-2);
1069
+ }
1070
+ .chat-error-hd {
1071
+ display: flex;
1072
+ align-items: center;
1073
+ gap: var(--space-2);
1074
+ font-family: var(--font-mono);
1075
+ font-size: var(--type-xs);
1076
+ color: var(--status-error);
1077
+ }
1078
+ .chat-error-detail {
1079
+ margin: 0;
1080
+ font-size: var(--type-xs);
1081
+ color: var(--fg-2);
1082
+ }
1083
+ .chat-error-detail--muted {
1084
+ color: var(--fg-3);
1085
+ }
1086
+ .chat-error-actions {
1087
+ display: flex;
1088
+ flex-wrap: wrap;
1089
+ gap: var(--space-2);
1090
+ }
1091
+
1092
+ /* Usage (Milestone D) — context-window bar, rendered inside .chat-foot-popover. */
1093
+ .chat-usage-bar {
1094
+ display: inline-block;
1095
+ width: 32px;
1096
+ height: 4px;
1097
+ border-radius: var(--radius-pill);
1098
+ background: var(--bg-4, var(--bg-3));
1099
+ overflow: hidden;
1100
+ flex-shrink: 0;
1101
+ }
1102
+ .chat-usage-bar-fill {
1103
+ display: block;
1104
+ height: 100%;
1105
+ background: var(--accent);
1106
+ border-radius: var(--radius-pill);
1107
+ }
1108
+ /* Full-width variant for the .chat-foot-popover context section (the
1109
+ 32px pill above is sized for an inline ambient indicator, not a form
1110
+ bar). */
1111
+ .chat-usage-bar--wide {
1112
+ display: block;
1113
+ width: 100%;
1114
+ height: 5px;
1115
+ }
1116
+
1117
+ /* Event-driven single-window rate-limit banner — rendered INSIDE
1118
+ .chat-composer's own padding (unlike .chat-perm/.chat-error-card, which
1119
+ are siblings of the composer at the thread-root level), so no extra
1120
+ horizontal margin here. */
1121
+ .chat-usage-banner {
1122
+ display: flex;
1123
+ align-items: center;
1124
+ gap: var(--space-2);
1125
+ margin: 0 0 var(--space-3);
1126
+ padding: var(--space-2) var(--space-3);
1127
+ border: 1px solid var(--border-default);
1128
+ border-radius: var(--radius-sm);
1129
+ background: var(--bg-2);
1130
+ color: var(--fg-1);
1131
+ font-size: var(--type-xs);
1132
+ }
1133
+ .chat-usage-banner-x {
1134
+ margin-left: auto;
1135
+ flex-shrink: 0;
1136
+ appearance: none;
1137
+ border: 0;
1138
+ background: transparent;
1139
+ color: var(--fg-3);
1140
+ cursor: pointer;
1141
+ font-size: var(--type-sm);
1142
+ line-height: 1;
1143
+ padding: 0 var(--space-1);
1144
+ }
1145
+ .chat-usage-banner-x:hover {
1146
+ color: var(--fg-0);
1147
+ }
611
1148
 
612
1149
  /* Quick-action row */
613
1150
  .chat-quick {
@@ -712,7 +1249,30 @@
712
1249
  .chat-toolbar-spacer {
713
1250
  flex: 1;
714
1251
  }
715
- /* Compact toolbar dropdowns — borderless, hover-revealed (Zed-style). */
1252
+ /* Model/effort/mode picker row (feature-acp-panel-dynamic-claude-code-
1253
+ capabilities) — a flex row of .chat-select dropdowns inside .chat-toolbar. */
1254
+ .chat-caps {
1255
+ display: flex;
1256
+ align-items: center;
1257
+ gap: var(--space-1);
1258
+ flex-wrap: wrap;
1259
+ }
1260
+ .chat-caps--connecting {
1261
+ font-family: var(--font-body);
1262
+ font-size: var(--type-xs);
1263
+ color: var(--fg-3);
1264
+ padding: 2px var(--space-2);
1265
+ }
1266
+ /* Compact toolbar dropdowns — borderless, hover-revealed (Zed-style).
1267
+ Without `field-sizing`, a closed <select> sizes itself to its WIDEST
1268
+ option (not the one it's currently showing) — Model ends up as wide as
1269
+ its longest model name even while displaying "Sonnet". That's what made
1270
+ 2-3 short picks unable to share a row in a narrow panel — each one was
1271
+ wider than it looked and forced onto its own wrapped line. `field-sizing:
1272
+ content` sizes it to what's actually shown instead; it degrades
1273
+ harmlessly where unsupported (the max-width + ellipsis below is that
1274
+ fallback net), and `flex-shrink: 0` keeps the (now much smaller) content
1275
+ width from being squeezed further by the toolbar row. */
716
1276
  .chat-select {
717
1277
  appearance: none;
718
1278
  font-family: var(--font-body);
@@ -723,6 +1283,13 @@
723
1283
  border-radius: var(--radius-xs);
724
1284
  padding: 2px var(--space-2);
725
1285
  cursor: pointer;
1286
+ flex-shrink: 0;
1287
+ field-sizing: content;
1288
+ min-width: 44px;
1289
+ max-width: 140px;
1290
+ overflow: hidden;
1291
+ text-overflow: ellipsis;
1292
+ white-space: nowrap;
726
1293
  transition:
727
1294
  border-color var(--dur-soft) var(--ease-out),
728
1295
  background var(--dur-soft) var(--ease-out),
@@ -795,16 +1362,86 @@
795
1362
  color: var(--fg-2);
796
1363
  font-family: var(--font-mono);
797
1364
  }
798
- .chat-foot {
1365
+ .chat-foot-spacer {
1366
+ flex: 1;
1367
+ }
1368
+ /* Composer footer info (Milestone D, revised twice after dogfooding — the
1369
+ keyboard hint + context gauge + mode footnote + subscription note first
1370
+ collapsed behind an icon on its own row, then that icon moved INTO
1371
+ .chat-toolbar next to Send so it costs no extra vertical space). The
1372
+ popover is grouped into labeled sections (context / cost / mode /
1373
+ meta) with dividers, rather than a flat stack of unrelated lines. */
1374
+ .chat-foot-info {
1375
+ position: relative;
1376
+ display: inline-flex;
1377
+ }
1378
+ .chat-foot-info-btn {
1379
+ appearance: none;
1380
+ border: 0;
1381
+ background: transparent;
1382
+ cursor: pointer;
1383
+ color: var(--fg-3);
1384
+ font-size: var(--type-sm);
1385
+ line-height: 1;
1386
+ padding: 2px var(--space-1);
1387
+ border-radius: var(--radius-xs);
1388
+ }
1389
+ .chat-foot-info-btn:hover,
1390
+ .chat-foot-info-btn[aria-expanded='true'] {
1391
+ color: var(--fg-1);
1392
+ background: var(--bg-3);
1393
+ }
1394
+ .chat-foot-info-btn:focus-visible {
1395
+ outline: none;
1396
+ border-color: var(--accent);
1397
+ box-shadow: 0 0 0 3px var(--accent-tint);
1398
+ }
1399
+ /* `position: fixed` (not absolute) — `.chat-foot-info` sits inside
1400
+ `.chat-box`, which clips its content with `overflow: hidden` for the
1401
+ composer's rounded-corner focus ring. An absolutely-positioned popover
1402
+ anchored there got clipped to that box instead of floating above the
1403
+ whole composer. `bottom`/`right` are computed from the button's own
1404
+ rect and set inline (see ChatFootInfo in ChatPanel.jsx). */
1405
+ .chat-foot-popover {
1406
+ position: fixed;
1407
+ z-index: 52;
799
1408
  display: flex;
800
- align-items: center;
801
- gap: var(--space-2);
802
- margin-top: var(--space-2);
1409
+ flex-direction: column;
1410
+ min-width: 240px;
1411
+ padding: var(--space-3);
1412
+ background: var(--bg-2);
1413
+ border: 1px solid var(--border-default);
1414
+ border-radius: var(--radius-sm);
1415
+ box-shadow: var(--shadow-lg);
1416
+ font-family: var(--font-mono);
803
1417
  font-size: var(--type-xs);
804
1418
  color: var(--fg-2);
805
1419
  }
806
- .chat-foot-spacer {
807
- flex: 1;
1420
+ .chat-foot-popover-section {
1421
+ display: flex;
1422
+ flex-direction: column;
1423
+ gap: 6px;
1424
+ }
1425
+ .chat-foot-popover-sep {
1426
+ height: 1px;
1427
+ background: var(--border-subtle);
1428
+ margin: var(--space-2) calc(var(--space-3) * -1);
1429
+ }
1430
+ .chat-foot-popover-label-row {
1431
+ display: flex;
1432
+ align-items: center;
1433
+ justify-content: space-between;
1434
+ gap: var(--space-2);
1435
+ color: var(--fg-0);
1436
+ font-weight: 600;
1437
+ }
1438
+ .chat-foot-popover-row--muted {
1439
+ color: var(--fg-3);
1440
+ }
1441
+ .chat-foot-popover-foot {
1442
+ display: flex;
1443
+ flex-direction: column;
1444
+ gap: 4px;
808
1445
  }
809
1446
 
810
1447
  /* ── Slash-command autocomplete + inline highlight ────────────────────────