omnidesign 1.0.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/LICENSE +21 -0
- package/QUICKREF.md +150 -0
- package/README.md +576 -0
- package/bin/cli.js +390 -0
- package/bin/detect-ide.js +50 -0
- package/bin/install.js +8 -0
- package/logo.jpg +0 -0
- package/package.json +84 -0
- package/recipes/components/README.md +29 -0
- package/recipes/components/agent-card.md +314 -0
- package/recipes/components/ai-chat.md +252 -0
- package/recipes/components/bento-grid.md +186 -0
- package/recipes/components/code-block.md +503 -0
- package/recipes/components/file-upload.md +483 -0
- package/recipes/components/forms.md +238 -0
- package/recipes/components/hero-section.md +161 -0
- package/recipes/components/navbar.md +214 -0
- package/recipes/components/prompt-input.md +293 -0
- package/recipes/components/thinking-indicator.md +372 -0
- package/recipes/motion/README.md +3 -0
- package/recipes/motion/motion-system.md +437 -0
- package/recipes/patterns/README.md +3 -0
- package/skills/aider/omnidesign.md +67 -0
- package/skills/amp/SKILL.md +114 -0
- package/skills/antigravity/SKILL.md +114 -0
- package/skills/claude/omnidesign.md +111 -0
- package/skills/continue/omnidesign.yaml +29 -0
- package/skills/cursor/omnidesign.md +110 -0
- package/skills/kilo/SKILL.md +114 -0
- package/skills/opencode/omnidesign.md +110 -0
- package/skills/vscode/package.json +66 -0
- package/skills/zed/omnidesign.json +7 -0
- package/tokens/motion/README.md +3 -0
- package/tokens/primitives/README.md +3 -0
- package/tokens/primitives/color.json +219 -0
- package/tokens/primitives/motion.json +56 -0
- package/tokens/primitives/radii.json +37 -0
- package/tokens/primitives/shadows.json +34 -0
- package/tokens/primitives/spacing.json +67 -0
- package/tokens/primitives/typography.json +127 -0
- package/tokens/semantic/README.md +3 -0
- package/tokens/semantic/color.json +114 -0
- package/tokens/semantic/motion.json +44 -0
- package/tokens/semantic/radii.json +29 -0
- package/tokens/semantic/shadows.json +24 -0
- package/tokens/semantic/spacing.json +69 -0
- package/tokens/semantic/typography.json +118 -0
- package/tokens/shadows/README.md +3 -0
- package/tokens/themes/README.md +3 -0
- package/tokens/themes/berry.json +143 -0
- package/tokens/themes/brutalist.json +143 -0
- package/tokens/themes/coral.json +143 -0
- package/tokens/themes/corporate.json +143 -0
- package/tokens/themes/cream.json +143 -0
- package/tokens/themes/cyberpunk.json +143 -0
- package/tokens/themes/daylight.json +143 -0
- package/tokens/themes/deep-space.json +143 -0
- package/tokens/themes/forest.json +143 -0
- package/tokens/themes/graphite.json +143 -0
- package/tokens/themes/lavender.json +143 -0
- package/tokens/themes/midnight.json +143 -0
- package/tokens/themes/mint.json +143 -0
- package/tokens/themes/navy.json +143 -0
- package/tokens/themes/noir.json +143 -0
- package/tokens/themes/obsidian.json +143 -0
- package/tokens/themes/ocean.json +143 -0
- package/tokens/themes/paper.json +143 -0
- package/tokens/themes/ruby.json +143 -0
- package/tokens/themes/slate.json +143 -0
- package/tokens/themes/snow.json +143 -0
- package/tokens/themes/solar.json +143 -0
- package/tokens/themes/spring.json +143 -0
- package/tokens/themes/starry-night.json +143 -0
- package/tokens/themes/sunset.json +143 -0
- package/tokens/typography/FONT_GUIDE.md +381 -0
- package/tokens/typography/README.md +37 -0
- package/tokens/typography/font-collection.json +221 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# AI Prompt Input
|
|
2
|
+
|
|
3
|
+
Smart input component with prompt suggestions, templates, and autocomplete.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
- AI image generation interfaces (Midjourney, DALL-E)
|
|
7
|
+
- Prompt engineering tools
|
|
8
|
+
- AI writing assistants
|
|
9
|
+
- Command palettes with AI
|
|
10
|
+
|
|
11
|
+
## Anatomy
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
15
|
+
│ PromptInput │
|
|
16
|
+
│ ├─ PromptField (textarea with expanding height) │
|
|
17
|
+
│ ├─ PromptToolbar (template buttons, modifiers) │
|
|
18
|
+
│ ├─ SuggestionsDropdown (autocomplete, history) │
|
|
19
|
+
│ ├─ TokenCounter (remaining tokens / cost estimate) │
|
|
20
|
+
│ └─ PromptExamples (preset prompts below input) │
|
|
21
|
+
└─────────────────────────────────────────────────────────────┘
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Token Usage
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
/* Main Input */
|
|
28
|
+
.prompt-input {
|
|
29
|
+
background: var(--color-surface-raised);
|
|
30
|
+
border: 2px solid var(--color-border-default);
|
|
31
|
+
border-radius: var(--radius-xl);
|
|
32
|
+
padding: var(--spacing-md);
|
|
33
|
+
transition: border-color var(--duration-fast);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.prompt-input:focus-within {
|
|
37
|
+
border-color: var(--color-interactive-primary);
|
|
38
|
+
box-shadow: var(--shadow-focus);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Textarea */
|
|
42
|
+
.prompt-textarea {
|
|
43
|
+
background: transparent;
|
|
44
|
+
border: none;
|
|
45
|
+
color: var(--color-text-default);
|
|
46
|
+
font-family: var(--font-sans);
|
|
47
|
+
font-size: var(--font-size-base);
|
|
48
|
+
line-height: var(--line-height-relaxed);
|
|
49
|
+
resize: none;
|
|
50
|
+
min-height: 3rem;
|
|
51
|
+
max-height: 20rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Suggestions Dropdown */
|
|
55
|
+
.suggestions {
|
|
56
|
+
background: var(--color-surface-raised);
|
|
57
|
+
border: 1px solid var(--color-border-default);
|
|
58
|
+
border-radius: var(--radius-lg);
|
|
59
|
+
box-shadow: var(--shadow-dropdown);
|
|
60
|
+
max-height: 300px;
|
|
61
|
+
overflow-y: auto;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.suggestion-item {
|
|
65
|
+
padding: var(--spacing-sm) var(--spacing-md);
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.suggestion-item:hover,
|
|
70
|
+
.suggestion-item[aria-selected="true"] {
|
|
71
|
+
background: var(--color-surface-sunken);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Token Counter */
|
|
75
|
+
.token-counter {
|
|
76
|
+
font-family: var(--font-mono);
|
|
77
|
+
font-size: var(--font-size-xs);
|
|
78
|
+
color: var(--color-text-muted);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.token-counter.warning {
|
|
82
|
+
color: var(--color-status-warning);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.token-counter.error {
|
|
86
|
+
color: var(--color-status-error);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Modifier Tags */
|
|
90
|
+
.modifier-tag {
|
|
91
|
+
background: var(--color-surface-sunken);
|
|
92
|
+
border: 1px solid var(--color-border-default);
|
|
93
|
+
border-radius: var(--radius-full);
|
|
94
|
+
padding: var(--spacing-2xs) var(--spacing-sm);
|
|
95
|
+
font-size: var(--font-size-xs);
|
|
96
|
+
font-family: var(--font-mono);
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.modifier-tag:hover {
|
|
101
|
+
background: var(--color-interactive-primary);
|
|
102
|
+
color: var(--color-text-inverted);
|
|
103
|
+
border-color: var(--color-interactive-primary);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## State Matrix
|
|
108
|
+
|
|
109
|
+
| Element | Default | Focus | Hover | Active | Disabled |
|
|
110
|
+
|---------|---------|-------|-------|--------|----------|
|
|
111
|
+
| Input Container | border-default | border-primary + shadow-focus | - | - | opacity-50 |
|
|
112
|
+
| Suggestion | transparent | selected bg | sunken bg | - | - |
|
|
113
|
+
| Modifier Tag | sunken bg | - | primary bg | - | - |
|
|
114
|
+
| Token Counter | muted | - | - | - | - |
|
|
115
|
+
| Generate Button | primary | primary-hover | - | pressed | - |
|
|
116
|
+
|
|
117
|
+
## Accessibility
|
|
118
|
+
|
|
119
|
+
```html
|
|
120
|
+
<div role="combobox" aria-expanded="false" aria-haspopup="listbox" aria-controls="suggestions-list">
|
|
121
|
+
<textarea
|
|
122
|
+
role="textbox"
|
|
123
|
+
aria-autocomplete="list"
|
|
124
|
+
aria-controls="suggestions-list"
|
|
125
|
+
aria-activedescendant=""
|
|
126
|
+
placeholder="Describe what you want to create..."
|
|
127
|
+
></textarea>
|
|
128
|
+
|
|
129
|
+
<ul id="suggestions-list" role="listbox" aria-label="Prompt suggestions">
|
|
130
|
+
<li role="option" id="suggestion-1">A futuristic city...</li>
|
|
131
|
+
<li role="option" id="suggestion-2">A serene landscape...</li>
|
|
132
|
+
</ul>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div aria-live="polite" aria-atomic="true" class="sr-only">
|
|
136
|
+
50 tokens remaining
|
|
137
|
+
</div>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Example: Image Generation Prompt
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
function ImagePromptInput({ onGenerate, maxTokens = 1000 }) {
|
|
144
|
+
const [prompt, setPrompt] = useState('');
|
|
145
|
+
const [tokens, setTokens] = useState(0);
|
|
146
|
+
const [showSuggestions, setShowSuggestions] = useState(false);
|
|
147
|
+
|
|
148
|
+
const modifiers = [
|
|
149
|
+
{ label: '--ar 16:9', desc: 'Aspect ratio' },
|
|
150
|
+
{ label: '--v 6', desc: 'Version 6' },
|
|
151
|
+
{ label: '--style raw', desc: 'Raw style' },
|
|
152
|
+
{ label: '--s 750', desc: 'Stylize' },
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className="prompt-input-container">
|
|
157
|
+
<div className="prompt-input" onClick={() => textareaRef.current?.focus()}>
|
|
158
|
+
<textarea
|
|
159
|
+
ref={textareaRef}
|
|
160
|
+
value={prompt}
|
|
161
|
+
onChange={handleInputChange}
|
|
162
|
+
onFocus={() => setShowSuggestions(true)}
|
|
163
|
+
placeholder="A futuristic city at sunset, cyberpunk style, neon lights reflecting on wet streets..."
|
|
164
|
+
rows={3}
|
|
165
|
+
/>
|
|
166
|
+
|
|
167
|
+
<div className="prompt-toolbar">
|
|
168
|
+
<div className="modifier-chips">
|
|
169
|
+
{modifiers.map(mod => (
|
|
170
|
+
<button
|
|
171
|
+
key={mod.label}
|
|
172
|
+
className="modifier-tag"
|
|
173
|
+
onClick={() => appendModifier(mod.label)}
|
|
174
|
+
title={mod.desc}
|
|
175
|
+
>
|
|
176
|
+
{mod.label}
|
|
177
|
+
</button>
|
|
178
|
+
))}
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<TokenCounter current={tokens} max={maxTokens} />
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{showSuggestions && (
|
|
186
|
+
<SuggestionsDropdown
|
|
187
|
+
query={prompt}
|
|
188
|
+
onSelect={setPrompt}
|
|
189
|
+
onClose={() => setShowSuggestions(false)}
|
|
190
|
+
/>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
<div className="prompt-examples">
|
|
194
|
+
<span className="label">Try:</span>
|
|
195
|
+
{EXAMPLE_PROMPTS.map(example => (
|
|
196
|
+
<button
|
|
197
|
+
key={example}
|
|
198
|
+
className="example-chip"
|
|
199
|
+
onClick={() => setPrompt(example)}
|
|
200
|
+
>
|
|
201
|
+
{example.slice(0, 40)}...
|
|
202
|
+
</button>
|
|
203
|
+
))}
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<button
|
|
207
|
+
className="generate-btn"
|
|
208
|
+
disabled={!prompt.trim() || tokens > maxTokens}
|
|
209
|
+
onClick={() => onGenerate(prompt)}
|
|
210
|
+
>
|
|
211
|
+
<SparklesIcon />
|
|
212
|
+
Generate Image
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function TokenCounter({ current, max }) {
|
|
219
|
+
const percentage = (current / max) * 100;
|
|
220
|
+
const status = percentage > 90 ? 'error' : percentage > 70 ? 'warning' : '';
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div className={`token-counter ${status}`}>
|
|
224
|
+
{current.toLocaleString()} / {max.toLocaleString()} tokens
|
|
225
|
+
{status === 'error' && ' (Limit exceeded)'}
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Example: Command Palette with AI
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
function AIPromptPalette({ isOpen, onClose, onExecute }) {
|
|
235
|
+
const [query, setQuery] = useState('');
|
|
236
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<Modal isOpen={isOpen} onClose={onClose} className="prompt-palette">
|
|
240
|
+
<div className="palette-input">
|
|
241
|
+
<CommandIcon className="input-icon" />
|
|
242
|
+
<input
|
|
243
|
+
type="text"
|
|
244
|
+
value={query}
|
|
245
|
+
onChange={e => setQuery(e.target.value)}
|
|
246
|
+
placeholder="Ask AI to do something..."
|
|
247
|
+
autoFocus
|
|
248
|
+
/>
|
|
249
|
+
<kbd className="shortcut">ESC</kbd>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div className="palette-suggestions">
|
|
253
|
+
<section>
|
|
254
|
+
<header>AI Actions</header>
|
|
255
|
+
<SuggestionItem
|
|
256
|
+
icon={<GenerateIcon />}
|
|
257
|
+
title="Generate code"
|
|
258
|
+
subtitle="Create a React component"
|
|
259
|
+
onClick={() => onExecute('generate', query)}
|
|
260
|
+
/>
|
|
261
|
+
<SuggestionItem
|
|
262
|
+
icon={<ExplainIcon />}
|
|
263
|
+
title="Explain this"
|
|
264
|
+
subtitle="Explain the selected code"
|
|
265
|
+
onClick={() => onExecute('explain', query)}
|
|
266
|
+
/>
|
|
267
|
+
</section>
|
|
268
|
+
|
|
269
|
+
<section>
|
|
270
|
+
<header>Recent Prompts</header>
|
|
271
|
+
{recentPrompts.map(prompt => (
|
|
272
|
+
<SuggestionItem
|
|
273
|
+
key={prompt.id}
|
|
274
|
+
icon={<HistoryIcon />}
|
|
275
|
+
title={prompt.text}
|
|
276
|
+
onClick={() => onExecute('repeat', prompt.text)}
|
|
277
|
+
/>
|
|
278
|
+
))}
|
|
279
|
+
</section>
|
|
280
|
+
</div>
|
|
281
|
+
</Modal>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Tokens Used
|
|
287
|
+
|
|
288
|
+
- **color**: surface-raised, surface-sunken, interactive-primary, text-default, text-inverted, text-muted, border-default, status-warning, status-error
|
|
289
|
+
- **spacing**: 2xs, sm, md
|
|
290
|
+
- **radii**: full, lg, xl
|
|
291
|
+
- **shadow**: focus, dropdown
|
|
292
|
+
- **typography**: font-sans, font-mono, size-xs, size-base
|
|
293
|
+
- **motion**: duration-fast
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# Streaming/Thinking Indicator
|
|
2
|
+
|
|
3
|
+
Visual feedback for AI processing, streaming responses, and loading states.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
- AI is generating a response
|
|
7
|
+
- Processing user input
|
|
8
|
+
- Loading model outputs
|
|
9
|
+
- File upload/processing
|
|
10
|
+
- Multi-step AI workflows
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
16
|
+
│ ThinkingIndicator │
|
|
17
|
+
│ ├─ Container (message bubble or inline) │
|
|
18
|
+
│ ├─ Avatar/Icon (AI avatar or status icon) │
|
|
19
|
+
│ ├─ Animation (dots, wave, pulse, shimmer) │
|
|
20
|
+
│ ├─ Status Text (optional: "Thinking...", "Processing") │
|
|
21
|
+
│ └─ Cancel Action (optional: stop generation) │
|
|
22
|
+
└─────────────────────────────────────────────────────────────┘
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Token Usage
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
/* Base Container */
|
|
29
|
+
.thinking-indicator {
|
|
30
|
+
display: flex;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: var(--spacing-sm);
|
|
33
|
+
padding: var(--spacing-md);
|
|
34
|
+
color: var(--color-text-muted);
|
|
35
|
+
font-size: var(--font-size-sm);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* Message Bubble Style */
|
|
39
|
+
.thinking-bubble {
|
|
40
|
+
background: var(--color-surface-raised);
|
|
41
|
+
border: 1px solid var(--color-border-default);
|
|
42
|
+
border-radius: var(--radius-lg) var(--radius-lg) var(--radius-lg) var(--radius-sm);
|
|
43
|
+
padding: var(--spacing-card-padding);
|
|
44
|
+
max-width: 80%;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Inline Style */
|
|
48
|
+
.thinking-inline {
|
|
49
|
+
display: inline-flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--spacing-xs);
|
|
52
|
+
margin-left: var(--spacing-xs);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Dots Animation */
|
|
56
|
+
.thinking-dots {
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: 4px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.thinking-dots span {
|
|
62
|
+
width: 8px;
|
|
63
|
+
height: 8px;
|
|
64
|
+
background: var(--color-interactive-primary);
|
|
65
|
+
border-radius: var(--radius-full);
|
|
66
|
+
animation: bounce 1.4s ease-in-out infinite both;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.thinking-dots span:nth-child(1) { animation-delay: -0.32s; }
|
|
70
|
+
.thinking-dots span:nth-child(2) { animation-delay: -0.16s; }
|
|
71
|
+
.thinking-dots span:nth-child(3) { animation-delay: 0s; }
|
|
72
|
+
|
|
73
|
+
@keyframes bounce {
|
|
74
|
+
0%, 80%, 100% { transform: scale(0); }
|
|
75
|
+
40% { transform: scale(1); }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Wave Animation */
|
|
79
|
+
.thinking-wave {
|
|
80
|
+
display: flex;
|
|
81
|
+
align-items: center;
|
|
82
|
+
gap: 3px;
|
|
83
|
+
height: 20px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.thinking-wave span {
|
|
87
|
+
width: 3px;
|
|
88
|
+
background: var(--color-interactive-primary);
|
|
89
|
+
border-radius: var(--radius-full);
|
|
90
|
+
animation: wave 1.2s ease-in-out infinite;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.thinking-wave span:nth-child(1) { height: 8px; animation-delay: -0.4s; }
|
|
94
|
+
.thinking-wave span:nth-child(2) { height: 12px; animation-delay: -0.3s; }
|
|
95
|
+
.thinking-wave span:nth-child(3) { height: 16px; animation-delay: -0.2s; }
|
|
96
|
+
.thinking-wave span:nth-child(4) { height: 12px; animation-delay: -0.1s; }
|
|
97
|
+
.thinking-wave span:nth-child(5) { height: 8px; animation-delay: 0s; }
|
|
98
|
+
|
|
99
|
+
@keyframes wave {
|
|
100
|
+
0%, 100% { transform: scaleY(0.5); }
|
|
101
|
+
50% { transform: scaleY(1); }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Pulse Ring */
|
|
105
|
+
.thinking-pulse {
|
|
106
|
+
position: relative;
|
|
107
|
+
width: 40px;
|
|
108
|
+
height: 40px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.thinking-pulse::before,
|
|
112
|
+
.thinking-pulse::after {
|
|
113
|
+
content: '';
|
|
114
|
+
position: absolute;
|
|
115
|
+
inset: 0;
|
|
116
|
+
border-radius: var(--radius-full);
|
|
117
|
+
background: var(--color-interactive-primary);
|
|
118
|
+
opacity: 0.6;
|
|
119
|
+
animation: pulse-ring 2s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.thinking-pulse::after {
|
|
123
|
+
animation-delay: 1s;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@keyframes pulse-ring {
|
|
127
|
+
0% { transform: scale(0.8); opacity: 1; }
|
|
128
|
+
100% { transform: scale(2); opacity: 0; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Shimmer Effect */
|
|
132
|
+
.thinking-shimmer {
|
|
133
|
+
background: linear-gradient(
|
|
134
|
+
90deg,
|
|
135
|
+
var(--color-surface-sunken) 25%,
|
|
136
|
+
var(--color-surface-raised) 50%,
|
|
137
|
+
var(--color-surface-sunken) 75%
|
|
138
|
+
);
|
|
139
|
+
background-size: 200% 100%;
|
|
140
|
+
border-radius: var(--radius-md);
|
|
141
|
+
animation: shimmer 1.5s infinite;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@keyframes shimmer {
|
|
145
|
+
0% { background-position: 200% 0; }
|
|
146
|
+
100% { background-position: -200% 0; }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Progress Steps */
|
|
150
|
+
.progress-steps {
|
|
151
|
+
display: flex;
|
|
152
|
+
gap: var(--spacing-sm);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.progress-step {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
gap: var(--spacing-xs);
|
|
159
|
+
padding: var(--spacing-xs) var(--spacing-sm);
|
|
160
|
+
border-radius: var(--radius-full);
|
|
161
|
+
font-size: var(--font-size-xs);
|
|
162
|
+
background: var(--color-surface-sunken);
|
|
163
|
+
color: var(--color-text-muted);
|
|
164
|
+
transition: all var(--duration-fast);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.progress-step.active {
|
|
168
|
+
background: rgba(37, 99, 235, 0.15);
|
|
169
|
+
color: var(--color-interactive-primary);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.progress-step.completed {
|
|
173
|
+
background: rgba(34, 197, 94, 0.15);
|
|
174
|
+
color: #22C55E;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.progress-step-icon {
|
|
178
|
+
width: 16px;
|
|
179
|
+
height: 16px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Cancel Button */
|
|
183
|
+
.cancel-btn {
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
gap: var(--spacing-xs);
|
|
187
|
+
padding: var(--spacing-xs) var(--spacing-sm);
|
|
188
|
+
background: var(--color-surface-sunken);
|
|
189
|
+
border: 1px solid var(--color-border-default);
|
|
190
|
+
border-radius: var(--radius-md);
|
|
191
|
+
font-size: var(--font-size-xs);
|
|
192
|
+
color: var(--color-text-muted);
|
|
193
|
+
cursor: pointer;
|
|
194
|
+
transition: all var(--duration-fast);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.cancel-btn:hover {
|
|
198
|
+
background: rgba(239, 68, 68, 0.1);
|
|
199
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
200
|
+
color: var(--color-status-error);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## State Matrix
|
|
205
|
+
|
|
206
|
+
| Element | Default | Active | Completed | Error |
|
|
207
|
+
|---------|---------|--------|-----------|-------|
|
|
208
|
+
| Dots | bounce animation | - | fade out | - |
|
|
209
|
+
| Wave | wave animation | - | fade out | - |
|
|
210
|
+
| Pulse | pulse-ring | - | - | - |
|
|
211
|
+
| Progress Step | muted | primary bg | green | red |
|
|
212
|
+
| Cancel Button | ghost | hover red | - | - |
|
|
213
|
+
|
|
214
|
+
## Accessibility
|
|
215
|
+
|
|
216
|
+
```html
|
|
217
|
+
<!-- Screen reader announcements -->
|
|
218
|
+
<div aria-live="polite" aria-atomic="true" class="sr-only">
|
|
219
|
+
Assistant is thinking
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div role="status" aria-label="AI is processing your request">
|
|
223
|
+
<div class="thinking-dots" aria-hidden="true">
|
|
224
|
+
<span></span><span></span><span></span>
|
|
225
|
+
</div>
|
|
226
|
+
<span class="sr-only">Thinking</span>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<!-- Progress with steps -->
|
|
230
|
+
<div role="progressbar" aria-valuemin="0" aria-valuemax="3" aria-valuenow="2" aria-label="Processing steps">
|
|
231
|
+
<div class="progress-steps">
|
|
232
|
+
<div class="progress-step completed" aria-label="Step 1: Analyzing request, completed">
|
|
233
|
+
<CheckIcon /> Analyzing
|
|
234
|
+
</div>
|
|
235
|
+
<div class="progress-step active" aria-label="Step 2: Generating response, in progress">
|
|
236
|
+
<Spinner /> Generating
|
|
237
|
+
</div>
|
|
238
|
+
<div class="progress-step" aria-label="Step 3: Formatting output, pending">
|
|
239
|
+
Formatting
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Example: Chat Thinking State
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
function ChatThinking({ onCancel }) {
|
|
249
|
+
return (
|
|
250
|
+
<article className="thinking-bubble" aria-live="polite">
|
|
251
|
+
<div className="thinking-indicator">
|
|
252
|
+
<AIAvatar className="avatar-small" />
|
|
253
|
+
|
|
254
|
+
<div className="thinking-dots" aria-hidden="true">
|
|
255
|
+
<span></span>
|
|
256
|
+
<span></span>
|
|
257
|
+
<span></span>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<span className="sr-only">Assistant is thinking</span>
|
|
261
|
+
|
|
262
|
+
{onCancel && (
|
|
263
|
+
<button
|
|
264
|
+
className="cancel-btn"
|
|
265
|
+
onClick={onCancel}
|
|
266
|
+
aria-label="Stop generating"
|
|
267
|
+
>
|
|
268
|
+
<StopIcon />
|
|
269
|
+
Stop
|
|
270
|
+
</button>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
</article>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Example: Multi-Step Processing
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
function ProcessingSteps({ steps, currentStep }) {
|
|
282
|
+
return (
|
|
283
|
+
<div className="processing-indicator">
|
|
284
|
+
<div className="progress-steps">
|
|
285
|
+
{steps.map((step, index) => {
|
|
286
|
+
const isCompleted = index < currentStep;
|
|
287
|
+
const isActive = index === currentStep;
|
|
288
|
+
|
|
289
|
+
return (
|
|
290
|
+
<div
|
|
291
|
+
key={step.id}
|
|
292
|
+
className={`progress-step ${isActive ? 'active' : ''} ${isCompleted ? 'completed' : ''}`}
|
|
293
|
+
aria-current={isActive ? 'step' : undefined}
|
|
294
|
+
>
|
|
295
|
+
{isCompleted ? (
|
|
296
|
+
<CheckIcon className="progress-step-icon" />
|
|
297
|
+
) : isActive ? (
|
|
298
|
+
<Spinner className="progress-step-icon" />
|
|
299
|
+
) : (
|
|
300
|
+
<span className="step-number">{index + 1}</span>
|
|
301
|
+
)}
|
|
302
|
+
<span>{step.label}</span>
|
|
303
|
+
</div>
|
|
304
|
+
);
|
|
305
|
+
})}
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{currentStep < steps.length && (
|
|
309
|
+
<p className="current-step-description">
|
|
310
|
+
{steps[currentStep].description}
|
|
311
|
+
</p>
|
|
312
|
+
)}
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Usage
|
|
318
|
+
const steps = [
|
|
319
|
+
{ id: 'analyze', label: 'Analyzing', description: 'Understanding your request...' },
|
|
320
|
+
{ id: 'search', label: 'Searching', description: 'Finding relevant information...' },
|
|
321
|
+
{ id: 'generate', label: 'Generating', description: 'Creating your response...' },
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
<ProcessingSteps steps={steps} currentStep={1} />
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Example: Inline Typing Indicator
|
|
328
|
+
|
|
329
|
+
```tsx
|
|
330
|
+
function InlineThinking() {
|
|
331
|
+
return (
|
|
332
|
+
<span className="thinking-inline" role="status" aria-label="AI is typing">
|
|
333
|
+
<span className="thinking-wave" aria-hidden="true">
|
|
334
|
+
<span></span>
|
|
335
|
+
<span></span>
|
|
336
|
+
<span></span>
|
|
337
|
+
<span></span>
|
|
338
|
+
<span></span>
|
|
339
|
+
</span>
|
|
340
|
+
</span>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Example: Skeleton Loading for AI
|
|
346
|
+
|
|
347
|
+
```tsx
|
|
348
|
+
function MessageSkeleton() {
|
|
349
|
+
return (
|
|
350
|
+
<div className="message-skeleton" aria-busy="true" aria-label="Loading message">
|
|
351
|
+
<div className="skeleton-header">
|
|
352
|
+
<div className="thinking-shimmer avatar-skeleton"></div>
|
|
353
|
+
<div className="thinking-shimmer name-skeleton"></div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
356
|
+
<div className="skeleton-content">
|
|
357
|
+
<div className="thinking-shimmer line-skeleton" style={{ width: '90%' }}></div>
|
|
358
|
+
<div className="thinking-shimmer line-skeleton" style={{ width: '75%' }}></div>
|
|
359
|
+
<div className="thinking-shimmer line-skeleton" style={{ width: '60%' }}></div>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Tokens Used
|
|
367
|
+
|
|
368
|
+
- **color**: surface-raised, surface-sunken, interactive-primary, text-muted, status-error, status-success
|
|
369
|
+
- **spacing**: xs, sm, md, card-padding
|
|
370
|
+
- **radii**: full, md, lg
|
|
371
|
+
- **typography**: size-xs, size-sm
|
|
372
|
+
- **motion**: duration-fast
|