@usefragments/ui 1.3.2 → 1.4.0
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.
- package/README.md +3 -1
- package/dist/assets/ui.css +1261 -849
- package/dist/chart.cjs +60 -24
- package/dist/chart.js +38 -24
- package/dist/codeblock.cjs +14 -6
- package/dist/codeblock.js +14 -6
- package/dist/colorpicker.cjs +56 -9
- package/dist/colorpicker.js +34 -9
- package/dist/components/Badge/Badge.module.scss.cjs +23 -16
- package/dist/components/Badge/Badge.module.scss.js +23 -16
- package/dist/components/Badge/index.cjs +8 -1
- package/dist/components/Badge/index.d.ts +3 -0
- package/dist/components/Badge/index.d.ts.map +1 -1
- package/dist/components/Badge/index.js +8 -1
- package/dist/components/Button/Button.module.scss.cjs +16 -13
- package/dist/components/Button/Button.module.scss.js +16 -13
- package/dist/components/Button/index.d.ts +2 -1
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Chart/index.d.ts +3 -0
- package/dist/components/Chart/index.d.ts.map +1 -1
- package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +21 -21
- package/dist/components/CodeBlock/CodeBlock.module.scss.js +21 -21
- package/dist/components/CodeBlock/index.d.ts.map +1 -1
- package/dist/components/ColorPicker/index.d.ts +4 -0
- package/dist/components/ColorPicker/index.d.ts.map +1 -1
- package/dist/components/Combobox/Combobox.module.scss.cjs +23 -23
- package/dist/components/Combobox/Combobox.module.scss.js +23 -23
- package/dist/components/Command/Command.module.scss.cjs +11 -11
- package/dist/components/Command/Command.module.scss.js +11 -11
- package/dist/components/ConversationList/ConversationList.module.scss.cjs +13 -10
- package/dist/components/ConversationList/ConversationList.module.scss.js +14 -11
- package/dist/components/ConversationList/index.cjs +26 -22
- package/dist/components/ConversationList/index.d.ts +5 -1
- package/dist/components/ConversationList/index.d.ts.map +1 -1
- package/dist/components/ConversationList/index.js +27 -23
- package/dist/components/DataTable/DataTable.module.scss.cjs +35 -35
- package/dist/components/DataTable/DataTable.module.scss.js +35 -35
- package/dist/components/DataTable/index.d.ts +4 -1
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DatePicker/index.d.ts +3 -0
- package/dist/components/DatePicker/index.d.ts.map +1 -1
- package/dist/components/Editor/index.d.ts +4 -1
- package/dist/components/Editor/index.d.ts.map +1 -1
- package/dist/components/Listbox/Listbox.module.scss.cjs +8 -8
- package/dist/components/Listbox/Listbox.module.scss.js +8 -8
- package/dist/components/Markdown/Markdown.module.scss.cjs +1 -1
- package/dist/components/Markdown/Markdown.module.scss.js +1 -1
- package/dist/components/Markdown/index.d.ts +16 -0
- package/dist/components/Markdown/index.d.ts.map +1 -1
- package/dist/components/Menu/Menu.module.scss.cjs +14 -14
- package/dist/components/Menu/Menu.module.scss.js +14 -14
- package/dist/components/Message/Message.module.scss.cjs +23 -20
- package/dist/components/Message/Message.module.scss.js +24 -21
- package/dist/components/Message/index.cjs +5 -1
- package/dist/components/Message/index.d.ts.map +1 -1
- package/dist/components/Message/index.js +5 -1
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.cjs +28 -28
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.js +28 -28
- package/dist/components/Pagination/Pagination.module.scss.cjs +7 -7
- package/dist/components/Pagination/Pagination.module.scss.js +7 -7
- package/dist/components/Prompt/Prompt.module.scss.cjs +52 -22
- package/dist/components/Prompt/Prompt.module.scss.js +52 -22
- package/dist/components/Prompt/index.cjs +197 -2
- package/dist/components/Prompt/index.d.ts +98 -3
- package/dist/components/Prompt/index.d.ts.map +1 -1
- package/dist/components/Prompt/index.js +198 -3
- package/dist/components/Select/Select.module.scss.cjs +29 -17
- package/dist/components/Select/Select.module.scss.js +30 -18
- package/dist/components/Select/index.cjs +45 -8
- package/dist/components/Select/index.d.ts +17 -2
- package/dist/components/Select/index.d.ts.map +1 -1
- package/dist/components/Select/index.js +45 -8
- package/dist/components/Sidebar/Sidebar.module.scss.cjs +45 -42
- package/dist/components/Sidebar/Sidebar.module.scss.js +45 -42
- package/dist/components/Sidebar/index.cjs +2 -0
- package/dist/components/Sidebar/index.d.ts +6 -1
- package/dist/components/Sidebar/index.d.ts.map +1 -1
- package/dist/components/Sidebar/index.js +2 -0
- package/dist/components/Text/Text.module.scss.cjs +63 -51
- package/dist/components/Text/Text.module.scss.js +63 -51
- package/dist/components/Text/index.d.ts +4 -2
- package/dist/components/Text/index.d.ts.map +1 -1
- package/dist/data-table.cjs +210 -28
- package/dist/data-table.js +189 -29
- package/dist/datepicker.cjs +63 -14
- package/dist/datepicker.js +41 -14
- package/dist/editor.cjs +67 -16
- package/dist/editor.js +45 -16
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/markdown.cjs +63 -19
- package/dist/markdown.js +41 -19
- package/fragments.json +1 -1
- package/package.json +2 -2
- package/src/components/Badge/Badge.contract.json +28 -5
- package/src/components/Badge/Badge.module.scss +21 -0
- package/src/components/Badge/index.tsx +10 -1
- package/src/components/Button/Button.contract.json +9 -3
- package/src/components/Button/Button.module.scss +34 -0
- package/src/components/Button/index.tsx +11 -1
- package/src/components/Chart/Chart.contract.json +7 -0
- package/src/components/Chart/index.tsx +44 -27
- package/src/components/CodeBlock/CodeBlock.contract.json +7 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +7 -2
- package/src/components/CodeBlock/index.tsx +38 -6
- package/src/components/ColorPicker/ColorPicker.contract.json +9 -1
- package/src/components/ColorPicker/index.tsx +42 -12
- package/src/components/Combobox/Combobox.module.scss +4 -13
- package/src/components/ConversationList/ConversationList.contract.json +16 -1
- package/src/components/ConversationList/ConversationList.module.scss +12 -0
- package/src/components/ConversationList/index.tsx +40 -26
- package/src/components/DataTable/DataTable.contract.json +8 -1
- package/src/components/DataTable/DataTable.module.scss +5 -0
- package/src/components/DataTable/index.tsx +282 -51
- package/src/components/DatePicker/DatePicker.contract.json +8 -1
- package/src/components/DatePicker/index.tsx +51 -22
- package/src/components/Editor/Editor.contract.json +19 -1
- package/src/components/Editor/index.tsx +67 -23
- package/src/components/Listbox/Listbox.module.scss +3 -11
- package/src/components/Markdown/Markdown.contract.json +15 -1
- package/src/components/Markdown/Markdown.module.scss +18 -1
- package/src/components/Markdown/index.tsx +58 -24
- package/src/components/Menu/Menu.module.scss +12 -2
- package/src/components/Message/Message.contract.json +3 -0
- package/src/components/Message/Message.module.scss +33 -4
- package/src/components/Message/index.tsx +7 -1
- package/src/components/Pagination/Pagination.contract.json +1 -0
- package/src/components/Pagination/Pagination.module.scss +5 -4
- package/src/components/Prompt/Prompt.contract.json +40 -1
- package/src/components/Prompt/Prompt.module.scss +351 -4
- package/src/components/Prompt/index.tsx +324 -0
- package/src/components/Select/Select.contract.json +11 -1
- package/src/components/Select/Select.module.scss +80 -13
- package/src/components/Select/index.tsx +73 -7
- package/src/components/Sidebar/Sidebar.contract.json +1 -0
- package/src/components/Sidebar/Sidebar.module.scss +10 -0
- package/src/components/Sidebar/index.tsx +7 -0
- package/src/components/Text/Text.contract.json +7 -4
- package/src/components/Text/Text.module.scss +18 -0
- package/src/components/Text/index.tsx +4 -2
- package/src/index.ts +1 -0
- package/src/tokens/_mixins.scss +39 -4
- package/src/tokens/_variables.scss +30 -0
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
"maxRows: number (default: 8)",
|
|
28
28
|
"autoResize: boolean (default: true)",
|
|
29
29
|
"submitOnEnter: boolean (default: true)",
|
|
30
|
-
"variant: default|fixed|sticky (default: default)"
|
|
30
|
+
"variant: default|fixed|sticky (default: default)",
|
|
31
|
+
"appearance: panel|seamless (default: panel)",
|
|
32
|
+
"onFiles: function",
|
|
33
|
+
"accept: string"
|
|
31
34
|
],
|
|
32
35
|
"props": {
|
|
33
36
|
"children": {
|
|
@@ -108,6 +111,26 @@
|
|
|
108
111
|
"fixed",
|
|
109
112
|
"sticky"
|
|
110
113
|
]
|
|
114
|
+
},
|
|
115
|
+
"onFiles": {
|
|
116
|
+
"type": "function",
|
|
117
|
+
"description": "Called with files added by the attach button, a paste, or a drop anywhere on the card. Providing it enables all three.",
|
|
118
|
+
"required": false
|
|
119
|
+
},
|
|
120
|
+
"accept": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"description": "accept filter for the attach button's picker, e.g. \"image/*\"",
|
|
123
|
+
"required": false
|
|
124
|
+
},
|
|
125
|
+
"appearance": {
|
|
126
|
+
"type": "enum",
|
|
127
|
+
"description": "How the card is divided up. \"panel\" keeps the toolbar as a filled footer under a rule; \"seamless\" makes the whole card one writing surface with the controls floating on it.",
|
|
128
|
+
"default": "panel",
|
|
129
|
+
"required": false,
|
|
130
|
+
"values": [
|
|
131
|
+
"panel",
|
|
132
|
+
"seamless"
|
|
133
|
+
]
|
|
111
134
|
}
|
|
112
135
|
},
|
|
113
136
|
"usage": {
|
|
@@ -126,6 +149,14 @@
|
|
|
126
149
|
"Use loading state during API calls",
|
|
127
150
|
"Prompt.Textarea forwards native textarea props (autoComplete, inputMode, aria-*, data-*, handlers)",
|
|
128
151
|
"Prompt.Textarea composes your onChange/onKeyDown handlers with built-in submit and auto-resize behavior",
|
|
152
|
+
"Use appearance=\"seamless\" for agent composers where the controls should float on the writing surface rather than sit in a footer",
|
|
153
|
+
"Seamless controls share the dense 32px token scale with body-sized labels and standard glyphs so mixed Prompt.Select, Prompt.Attach and Prompt.Submit rows remain optically aligned",
|
|
154
|
+
"Seamless secondary controls keep transparent hover and open states so they read as inline tools; only Prompt.Submit carries a persistent filled shape",
|
|
155
|
+
"Seamless Prompt.Submit mirrors the trailing ink gutter established by Prompt.Attach at the leading edge",
|
|
156
|
+
"Prompt.Submit uses a neutral inverse surface by default so the primary action stays visible without consuming semantic brand color",
|
|
157
|
+
"Use Prompt.ModeButton for a setting you toggle and Prompt.Select for one you pick from a list",
|
|
158
|
+
"Wire onFiles once: the attach button, pasting a screenshot and dropping on the card all go through it",
|
|
159
|
+
"Render Prompt.Attachments above Prompt.Textarea — attachments are part of the message, not a setting on it",
|
|
129
160
|
"Consider showing usage/token limits for AI contexts"
|
|
130
161
|
],
|
|
131
162
|
"accessibility": [
|
|
@@ -150,6 +181,11 @@
|
|
|
150
181
|
"description": "Shows token usage indicator",
|
|
151
182
|
"code": "<Prompt onSubmit={(value) => console.log(value)}>\n <Prompt.Textarea />\n <Prompt.Toolbar>\n <Prompt.Actions>\n <Prompt.ActionButton aria-label=\"Add attachment\">\n +\n </Prompt.ActionButton>\n <Prompt.ModeButton active>Auto</Prompt.ModeButton>\n </Prompt.Actions>\n <Prompt.Info>\n <Prompt.Usage>52% used</Prompt.Usage>\n <Prompt.Submit />\n </Prompt.Info>\n </Prompt.Toolbar>\n</Prompt>"
|
|
152
183
|
},
|
|
184
|
+
{
|
|
185
|
+
"name": "Agent Composer",
|
|
186
|
+
"description": "Seamless surface with the scope of the run chosen on the toolbar",
|
|
187
|
+
"code": "<Prompt\n appearance=\"seamless\"\n submitOnEnter={false}\n minRows={3}\n onSubmit={(value) => console.log(value)}\n>\n <Prompt.Textarea placeholder=\"What should be true when this is done?\" />\n <Prompt.Toolbar>\n <Prompt.Actions>\n <Prompt.Select\n aria-label=\"Model\"\n defaultValue=\"claude-opus-4-8\"\n options={[{ value: 'claude-opus-4-8', label: 'Opus 4.8' }, { value: 'claude-sonnet-5', label: 'Sonnet 5' }]}\n />\n <Prompt.ModeButton>Plan</Prompt.ModeButton>\n </Prompt.Actions>\n <Prompt.Info>\n <Prompt.Submit />\n </Prompt.Info>\n </Prompt.Toolbar>\n</Prompt>"
|
|
188
|
+
},
|
|
153
189
|
{
|
|
154
190
|
"name": "Loading State",
|
|
155
191
|
"description": "During API submission",
|
|
@@ -200,6 +236,9 @@
|
|
|
200
236
|
"Info",
|
|
201
237
|
"ActionButton",
|
|
202
238
|
"ModeButton",
|
|
239
|
+
"Select",
|
|
240
|
+
"Attach",
|
|
241
|
+
"Attachments",
|
|
203
242
|
"Usage",
|
|
204
243
|
"Submit"
|
|
205
244
|
]
|
|
@@ -113,6 +113,198 @@
|
|
|
113
113
|
transform: translateX(-50%);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// ============================================
|
|
117
|
+
// Seamless appearance
|
|
118
|
+
// ============================================
|
|
119
|
+
|
|
120
|
+
// One continuous writing surface instead of a field with a footer.
|
|
121
|
+
//
|
|
122
|
+
// The whole point is that nothing inside the card announces itself: no rule
|
|
123
|
+
// under the toolbar, no stroke around the edge, no animated border. What is
|
|
124
|
+
// left is a single quiet sheet, lighter than the page, with the controls
|
|
125
|
+
// resting on it — which is what makes the text you are writing the only thing
|
|
126
|
+
// with any weight. Each selector below is one class deeper than the rule it
|
|
127
|
+
// replaces, so this wins wherever the stylesheet happens to land.
|
|
128
|
+
//
|
|
129
|
+
// One gutter runs down the card. `--_gutter` is the distance from the card's
|
|
130
|
+
// edge to the ink of everything in it, and every child is padded from it
|
|
131
|
+
// rather than given a number of its own, so the placeholder, the first glyph
|
|
132
|
+
// in the toolbar and any attachment chip all sit on the same line.
|
|
133
|
+
.seamless {
|
|
134
|
+
--_gutter: var(--fui-space-3, #{$fui-space-3});
|
|
135
|
+
// Every band is inset by its own children's padding, so what lands on the
|
|
136
|
+
// gutter is the ink — the first character of the placeholder, the first glyph
|
|
137
|
+
// in the toolbar, the first pixel of a thumbnail — and not the invisible box
|
|
138
|
+
// around it. Controls and chips carry different padding, so each band
|
|
139
|
+
// subtracts its own.
|
|
140
|
+
--_control-padding: var(--fui-space-2, #{$fui-space-2});
|
|
141
|
+
--_chip-padding: var(--fui-space-1, #{$fui-space-1});
|
|
142
|
+
|
|
143
|
+
// Composer controls are compact, but still need enough visual weight to read
|
|
144
|
+
// as one deliberate toolbar. The xs button token is the dense 32px control
|
|
145
|
+
// height in every density preset; scoping all three inputs to it keeps
|
|
146
|
+
// selects, attach, action and submit controls on one optical grid without a
|
|
147
|
+
// one-off dimension.
|
|
148
|
+
--fui-target-size-min: var(--fui-button-height-xs, #{$fui-button-height-xs});
|
|
149
|
+
--fui-button-height-sm: var(--fui-button-height-xs, #{$fui-button-height-xs});
|
|
150
|
+
--fui-input-height-sm: var(--fui-button-height-xs, #{$fui-button-height-xs});
|
|
151
|
+
|
|
152
|
+
border-color: transparent;
|
|
153
|
+
border-radius: var(--fui-radius-xl, $fui-radius-xl);
|
|
154
|
+
|
|
155
|
+
// The beam draws a 1px animated border by painting a gradient and masking
|
|
156
|
+
// out everything but the edge. Seamless has no edge to draw, so both layers
|
|
157
|
+
// go and the card paints its own surface instead of borrowing the mask's.
|
|
158
|
+
&::before,
|
|
159
|
+
&::after {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Focus lives in the caret and in the fact that the surface is the thing you
|
|
164
|
+
// are typing on. A ring here would be the loudest object on the page.
|
|
165
|
+
&:focus-within {
|
|
166
|
+
border-color: transparent;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.toolbar {
|
|
170
|
+
background-color: transparent;
|
|
171
|
+
border-top: none;
|
|
172
|
+
padding: 0 calc(var(--_gutter) - var(--_control-padding)) var(--_gutter);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// The message is the content of the card, not a field in it. The bottom is
|
|
176
|
+
// tight because the toolbar below is part of the same surface rather than a
|
|
177
|
+
// footer under it, and the top is tight for the same reason when a scope
|
|
178
|
+
// band sits above.
|
|
179
|
+
.textarea {
|
|
180
|
+
padding: var(--_gutter) var(--_gutter) var(--fui-space-1, $fui-space-1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.attachments {
|
|
184
|
+
padding: var(--_gutter) calc(var(--_gutter) - var(--_chip-padding)) 0;
|
|
185
|
+
gap: var(--fui-space-1, $fui-space-1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Two bands in a row would otherwise stack their gutters into a gap twice
|
|
189
|
+
// the size of the one at the card's edge.
|
|
190
|
+
.attachments + .textarea,
|
|
191
|
+
.tabs + .attachments {
|
|
192
|
+
padding-top: var(--fui-space-1, $fui-space-1);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// In a panel the tabs are a strip the card is docked to, so they are centred
|
|
196
|
+
// and bordered like a folder tab. Seamless has no card edge for them to dock
|
|
197
|
+
// against, so they become the top band of the one surface: left-aligned on
|
|
198
|
+
// the gutter with the rest of it, and stripped of the chrome that only made
|
|
199
|
+
// sense as an edge.
|
|
200
|
+
.tabs {
|
|
201
|
+
margin-bottom: 0;
|
|
202
|
+
justify-content: flex-start;
|
|
203
|
+
padding: var(--_gutter) calc(var(--_gutter) - var(--_control-padding)) 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.tabsInner {
|
|
207
|
+
background-color: transparent;
|
|
208
|
+
border: none;
|
|
209
|
+
border-radius: 0;
|
|
210
|
+
padding: 0;
|
|
211
|
+
gap: var(--fui-space-1, $fui-space-1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// The band above already sets the top of the card, so the message starts
|
|
215
|
+
// from the tighter of the two spacings rather than adding a second gutter.
|
|
216
|
+
.tabs + .textarea {
|
|
217
|
+
padding-top: var(--fui-space-1, $fui-space-1);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// A centred glyph in a square box puts its ink half a box in from the
|
|
221
|
+
// gutter, which reads as the one thing in the card that is out of line.
|
|
222
|
+
// Giving it the same inline padding as its neighbours puts the glyph where
|
|
223
|
+
// the placeholder above it starts, and costs only a wider hover.
|
|
224
|
+
.attach {
|
|
225
|
+
width: auto;
|
|
226
|
+
padding-inline: var(--_control-padding);
|
|
227
|
+
|
|
228
|
+
svg {
|
|
229
|
+
width: var(--fui-icon-md, $fui-icon-md);
|
|
230
|
+
height: var(--fui-icon-md, $fui-icon-md);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Labels and glyphs share the body scale inside a seamless composer. The
|
|
235
|
+
// trigger still truncates at its cap, but no longer looks like caption text
|
|
236
|
+
// floating between larger attach and submit affordances.
|
|
237
|
+
.select,
|
|
238
|
+
.modeButton {
|
|
239
|
+
font-size: var(--fui-font-size-sm, $fui-font-size-sm);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.select {
|
|
243
|
+
svg {
|
|
244
|
+
width: var(--fui-icon-md, $fui-icon-md);
|
|
245
|
+
height: var(--fui-icon-md, $fui-icon-md);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// These are settings on the message, not a row of buttons. Preserve their
|
|
250
|
+
// full target boxes for hit testing, but keep those boxes visually absent.
|
|
251
|
+
// Send remains the only persistent shape in the composer.
|
|
252
|
+
.select,
|
|
253
|
+
.attach,
|
|
254
|
+
.modeButton {
|
|
255
|
+
&:hover:not(:disabled):not([data-disabled]),
|
|
256
|
+
&:active:not(:disabled):not([data-disabled]) {
|
|
257
|
+
background-color: transparent;
|
|
258
|
+
color: var(--fui-text-primary, $fui-text-primary);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Keyboard focus still needs a visible location, but a restrained baseline
|
|
262
|
+
// reads as focus on inline content rather than another rounded button.
|
|
263
|
+
&:focus-visible {
|
|
264
|
+
outline: none;
|
|
265
|
+
box-shadow: inset 0 calc(var(--fui-space-px, #{$fui-space-px}) * -2) 0
|
|
266
|
+
var(--fui-text-primary, #{$fui-text-primary});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.select {
|
|
271
|
+
// Opening a menu identifies the active choice through its popup and text
|
|
272
|
+
// color. A held surface behind the trigger is unnecessary visual chrome.
|
|
273
|
+
&[data-popup-open] {
|
|
274
|
+
border-color: transparent;
|
|
275
|
+
background-color: transparent;
|
|
276
|
+
box-shadow: none;
|
|
277
|
+
color: var(--fui-text-primary, $fui-text-primary);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Pointer focus must not inherit the field shell's accent treatment.
|
|
281
|
+
&:focus:not(:focus-visible) {
|
|
282
|
+
border-color: transparent;
|
|
283
|
+
box-shadow: none;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Round affordances track the scoped target size rather than the 2rem they
|
|
288
|
+
// are given for a form.
|
|
289
|
+
.submit,
|
|
290
|
+
.actionButton {
|
|
291
|
+
width: var(--fui-target-size-min);
|
|
292
|
+
height: var(--fui-target-size-min);
|
|
293
|
+
|
|
294
|
+
svg {
|
|
295
|
+
width: var(--fui-icon-md, $fui-icon-md);
|
|
296
|
+
height: var(--fui-icon-md, $fui-icon-md);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Attach's glyph reaches the leading ink gutter through its own control
|
|
301
|
+
// padding. Give the filled Submit shape the same inset at the trailing edge
|
|
302
|
+
// so the two visible affordances balance across the toolbar.
|
|
303
|
+
.submit {
|
|
304
|
+
margin-inline-end: var(--_control-padding);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
116
308
|
// ============================================
|
|
117
309
|
// Textarea
|
|
118
310
|
// ============================================
|
|
@@ -292,6 +484,159 @@
|
|
|
292
484
|
background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
|
|
293
485
|
}
|
|
294
486
|
|
|
487
|
+
// ============================================
|
|
488
|
+
// Attach (bare icon control)
|
|
489
|
+
// ============================================
|
|
490
|
+
|
|
491
|
+
// A `+` with nothing around it. ActionButton is a filled circle, which is the
|
|
492
|
+
// right weight for a primary affordance in the bar; attaching a file is not
|
|
493
|
+
// one — it should be findable and otherwise silent, so this is the mode button
|
|
494
|
+
// with a square footprint and no label.
|
|
495
|
+
.attach {
|
|
496
|
+
@include button-reset;
|
|
497
|
+
@include interactive-base;
|
|
498
|
+
|
|
499
|
+
display: flex;
|
|
500
|
+
align-items: center;
|
|
501
|
+
justify-content: center;
|
|
502
|
+
flex-shrink: 0;
|
|
503
|
+
// The target size rather than a spacing step, so it tracks whatever scale
|
|
504
|
+
// the surface around it has established.
|
|
505
|
+
width: var(--fui-target-size-min, $fui-target-size-min);
|
|
506
|
+
height: var(--fui-target-size-min, $fui-target-size-min);
|
|
507
|
+
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
508
|
+
background-color: transparent;
|
|
509
|
+
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
510
|
+
|
|
511
|
+
&:hover:not(:disabled) {
|
|
512
|
+
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
513
|
+
color: var(--fui-text-primary, $fui-text-primary);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
&:active:not(:disabled) {
|
|
517
|
+
background-color: var(--fui-bg-active, $fui-bg-active);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
&:disabled {
|
|
521
|
+
opacity: 0.5;
|
|
522
|
+
cursor: not-allowed;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
svg {
|
|
526
|
+
width: var(--fui-icon-sm, $fui-icon-sm);
|
|
527
|
+
height: var(--fui-icon-sm, $fui-icon-sm);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ============================================
|
|
532
|
+
// Attachments strip
|
|
533
|
+
// ============================================
|
|
534
|
+
|
|
535
|
+
.attachments {
|
|
536
|
+
display: flex;
|
|
537
|
+
flex-wrap: wrap;
|
|
538
|
+
gap: var(--fui-space-1, $fui-space-1);
|
|
539
|
+
margin: 0;
|
|
540
|
+
padding: var(--fui-space-2, $fui-space-2) var(--fui-space-3, $fui-space-3) 0;
|
|
541
|
+
list-style: none;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// What is attached, stated once and removable. Sits above the message rather
|
|
545
|
+
// than below the toolbar, because it is part of what you are sending, not a
|
|
546
|
+
// setting on the sending.
|
|
547
|
+
// Sized off the same target as everything else in the bar, so a chip and a
|
|
548
|
+
// control are the same height and the card has one horizontal rhythm.
|
|
549
|
+
.attachment {
|
|
550
|
+
--_chip-height: var(--fui-target-size-min, #{$fui-target-size-min});
|
|
551
|
+
|
|
552
|
+
display: inline-flex;
|
|
553
|
+
align-items: center;
|
|
554
|
+
gap: var(--fui-space-1, $fui-space-1);
|
|
555
|
+
height: var(--_chip-height);
|
|
556
|
+
max-width: 15rem;
|
|
557
|
+
padding-inline: var(--fui-space-1, $fui-space-1);
|
|
558
|
+
// Outlined rather than filled. A chip sits on whatever surface the prompt is
|
|
559
|
+
// using, and on an elevated card the raised fill is the same value as the
|
|
560
|
+
// card itself — so the object would disappear exactly where it is most
|
|
561
|
+
// likely to be used. An edge reads on any of them.
|
|
562
|
+
border: 1px solid var(--fui-border, $fui-border);
|
|
563
|
+
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
564
|
+
background-color: var(--fui-bg-tertiary, $fui-bg-tertiary);
|
|
565
|
+
font-size: var(--fui-font-size-xs, $fui-font-size-xs);
|
|
566
|
+
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// A screenshot is recognised by its picture, not by "Screenshot 2026-07-24 at
|
|
570
|
+
// 22.06.09.png", so an image attachment leads with a thumbnail. It fills the
|
|
571
|
+
// chip's height less the padding, which keeps it square without a second
|
|
572
|
+
// number to keep in step.
|
|
573
|
+
.attachmentThumb {
|
|
574
|
+
width: calc(var(--_chip-height) - var(--fui-space-1, #{$fui-space-1}));
|
|
575
|
+
height: calc(var(--_chip-height) - var(--fui-space-1, #{$fui-space-1}));
|
|
576
|
+
flex-shrink: 0;
|
|
577
|
+
border-radius: var(--fui-radius-sm, $fui-radius-sm);
|
|
578
|
+
object-fit: cover;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// The name is the only part worth the width, so it is the only part that grows
|
|
582
|
+
// — and the only part that gives way when there is none.
|
|
583
|
+
.attachmentName {
|
|
584
|
+
flex: 1;
|
|
585
|
+
min-width: 0;
|
|
586
|
+
overflow: hidden;
|
|
587
|
+
text-overflow: ellipsis;
|
|
588
|
+
white-space: nowrap;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.attachmentSize {
|
|
592
|
+
flex-shrink: 0;
|
|
593
|
+
color: var(--fui-text-tertiary, $fui-text-tertiary);
|
|
594
|
+
font-variant-numeric: tabular-nums;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.attachmentRemove {
|
|
598
|
+
@include button-reset;
|
|
599
|
+
@include interactive-base;
|
|
600
|
+
|
|
601
|
+
display: flex;
|
|
602
|
+
align-items: center;
|
|
603
|
+
justify-content: center;
|
|
604
|
+
flex-shrink: 0;
|
|
605
|
+
width: calc(var(--_chip-height) - var(--fui-space-1, #{$fui-space-1}));
|
|
606
|
+
height: calc(var(--_chip-height) - var(--fui-space-1, #{$fui-space-1}));
|
|
607
|
+
border-radius: var(--fui-radius-sm, $fui-radius-sm);
|
|
608
|
+
background-color: transparent;
|
|
609
|
+
color: var(--fui-text-tertiary, $fui-text-tertiary);
|
|
610
|
+
|
|
611
|
+
&:hover {
|
|
612
|
+
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
613
|
+
color: var(--fui-text-primary, $fui-text-primary);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
svg {
|
|
617
|
+
width: var(--fui-icon-xs, $fui-icon-xs);
|
|
618
|
+
height: var(--fui-icon-xs, $fui-icon-xs);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Dropping a file anywhere on the card counts. The whole surface lights rather
|
|
623
|
+
// than a dashed rectangle appearing inside it, because the target is the
|
|
624
|
+
// composer, not some region of it.
|
|
625
|
+
.dragging {
|
|
626
|
+
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// ============================================
|
|
630
|
+
// Select (ghost Select, sized for the toolbar)
|
|
631
|
+
// ============================================
|
|
632
|
+
|
|
633
|
+
// The chosen value can be arbitrarily long — a model name, a file path — and
|
|
634
|
+
// must not push the submit button off the row. Select's ghost trigger already
|
|
635
|
+
// allows shrinking and ellipsises its value; this only caps how far it grows.
|
|
636
|
+
.select {
|
|
637
|
+
max-width: 15rem;
|
|
638
|
+
}
|
|
639
|
+
|
|
295
640
|
// ============================================
|
|
296
641
|
// Usage indicator
|
|
297
642
|
// ============================================
|
|
@@ -315,15 +660,17 @@
|
|
|
315
660
|
width: 2rem;
|
|
316
661
|
height: 2rem;
|
|
317
662
|
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
318
|
-
|
|
319
|
-
|
|
663
|
+
// A neutral inverse keeps send visibly primary without spending the product
|
|
664
|
+
// accent, which may already encode agent state.
|
|
665
|
+
background-color: var(--fui-text-primary, #{$fui-text-primary});
|
|
666
|
+
color: var(--fui-bg-elevated, #{$fui-bg-elevated});
|
|
320
667
|
|
|
321
668
|
&:hover:not(:disabled) {
|
|
322
|
-
background-color: var(--fui-
|
|
669
|
+
background-color: var(--fui-text-secondary, #{$fui-text-secondary});
|
|
323
670
|
}
|
|
324
671
|
|
|
325
672
|
&:active:not(:disabled) {
|
|
326
|
-
background-color: var(--fui-
|
|
673
|
+
background-color: var(--fui-text-tertiary, #{$fui-text-tertiary});
|
|
327
674
|
}
|
|
328
675
|
|
|
329
676
|
&:disabled {
|