omnira-ui 0.6.5 → 0.6.6

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.
@@ -487,6 +487,11 @@ const PAGE_BUNDLES = {
487
487
  // ── Shared pages bundles ──
488
488
  "login": ["AuthPage", "LoginSimple", "LoginSplitImage", "LoginSplitQuote", "LoginCardSeparated", "SocialButton", "Button", "Input", "Checkbox"],
489
489
  "sign-up": ["SignUpSimple", "SignUpSplitImage", "SignUpSplitQuote", "SignUpCardSeparated", "SocialButton", "Button", "Input", "Checkbox"],
490
+ "verification": ["VerificationPage", "Button", "PinInput"],
491
+ "forgot-password": ["ForgotPassword", "Button", "Input", "Checkbox"],
492
+ "not-found-page": ["NotFoundPage", "Button"],
493
+ "404": ["NotFoundPage", "Button"],
494
+ "email-template": ["EmailTemplate"],
490
495
  };
491
496
 
492
497
  // ── Add command — copy a single component ───────────────────────────
@@ -8,13 +8,13 @@
8
8
  background: var(--color-bg-card);
9
9
  border: 1px solid var(--color-border-standard);
10
10
  border-radius: var(--radius-xl);
11
- padding: 24px;
11
+ padding: 20px;
12
12
  }
13
13
 
14
14
  /* ── Item ── */
15
15
  .item {
16
16
  display: flex;
17
- gap: 12px;
17
+ gap: 10px;
18
18
  position: relative;
19
19
  }
20
20
 
@@ -32,27 +32,27 @@
32
32
  flex-direction: column;
33
33
  align-items: center;
34
34
  flex-shrink: 0;
35
- width: 36px;
35
+ width: 30px;
36
36
  }
37
37
 
38
38
  .avatar {
39
- width: 36px;
40
- height: 36px;
39
+ width: 30px;
40
+ height: 30px;
41
41
  border-radius: 50%;
42
42
  object-fit: cover;
43
43
  flex-shrink: 0;
44
44
  }
45
45
 
46
46
  .avatarFallback {
47
- width: 36px;
48
- height: 36px;
47
+ width: 30px;
48
+ height: 30px;
49
49
  border-radius: 50%;
50
50
  background: var(--color-bg-elevated);
51
51
  border: 1px solid var(--color-border-standard);
52
52
  display: flex;
53
53
  align-items: center;
54
54
  justify-content: center;
55
- font-size: 12px;
55
+ font-size: 11px;
56
56
  font-weight: 600;
57
57
  color: var(--color-text-secondary);
58
58
  flex-shrink: 0;
@@ -60,8 +60,8 @@
60
60
  }
61
61
 
62
62
  .iconBubble {
63
- width: 36px;
64
- height: 36px;
63
+ width: 30px;
64
+ height: 30px;
65
65
  border-radius: 50%;
66
66
  background: var(--color-bg-lime-subtle);
67
67
  display: flex;
@@ -74,20 +74,20 @@
74
74
  .connector {
75
75
  width: 2px;
76
76
  flex: 1;
77
- min-height: 24px;
77
+ min-height: 20px;
78
78
  background: var(--color-border-standard);
79
79
  }
80
80
 
81
81
  /* ── Compact ── */
82
82
  .compact .timeline {
83
- width: 28px;
83
+ width: 24px;
84
84
  }
85
85
 
86
86
  .compact .avatar,
87
87
  .compact .avatarFallback,
88
88
  .compact .iconBubble {
89
- width: 28px;
90
- height: 28px;
89
+ width: 24px;
90
+ height: 24px;
91
91
  }
92
92
 
93
93
  .compact .avatarFallback {
@@ -95,18 +95,18 @@
95
95
  }
96
96
 
97
97
  .compact .connector {
98
- min-height: 16px;
98
+ min-height: 12px;
99
99
  }
100
100
 
101
101
  /* ── Content Column ── */
102
102
  .content {
103
103
  flex: 1;
104
104
  min-width: 0;
105
- padding-bottom: 24px;
105
+ padding-bottom: 18px;
106
106
  }
107
107
 
108
108
  .compact .content {
109
- padding-bottom: 16px;
109
+ padding-bottom: 12px;
110
110
  }
111
111
 
112
112
  .item:last-child .content {
@@ -122,8 +122,8 @@
122
122
  }
123
123
 
124
124
  .userName {
125
- font-size: 14px;
126
- font-weight: 600;
125
+ font-size: 13px;
126
+ font-weight: 500;
127
127
  color: var(--color-text-primary);
128
128
  }
129
129
 
@@ -133,7 +133,7 @@
133
133
  }
134
134
 
135
135
  .body {
136
- font-size: 14px;
136
+ font-size: 13px;
137
137
  color: var(--color-text-secondary);
138
138
  line-height: 1.5;
139
139
  }
@@ -120,6 +120,8 @@
120
120
  position: relative;
121
121
  flex-shrink: 0;
122
122
  user-select: none;
123
+ min-width: 160px;
124
+ height: 42px;
123
125
  }
124
126
 
125
127
  .chip:hover {
@@ -217,6 +219,10 @@
217
219
  min-width: 0;
218
220
  position: relative;
219
221
  z-index: 1;
222
+ flex: 1;
223
+ overflow: hidden;
224
+ height: 28px;
225
+ justify-content: center;
220
226
  }
221
227
 
222
228
  .chipName {
@@ -257,6 +263,7 @@
257
263
  display: flex;
258
264
  align-items: center;
259
265
  gap: 0;
266
+ height: 14px;
260
267
  }
261
268
 
262
269
  .cursor {
@@ -7,33 +7,39 @@ import type { AgentConfig, AgentActivity, AgentState } from "./types";
7
7
  import s from "./AgentThinking.module.css";
8
8
 
9
9
  /* ══════════════════════════════════════════════
10
- Typewriter hook
10
+ Text cycling hook — fixed-size status rotation
11
11
  ══════════════════════════════════════════════ */
12
12
 
13
- function useTypewriter(text: string, isActive: boolean, speed = 30) {
14
- const [displayed, setDisplayed] = useState("");
15
- const indexRef = useRef(0);
13
+ const STATUS_PHRASES = [
14
+ "Analyzing...",
15
+ "Taking action...",
16
+ "Sending emails...",
17
+ "On a phone call...",
18
+ "Reflecting...",
19
+ "Processing data...",
20
+ "Searching...",
21
+ "Composing response...",
22
+ ];
23
+
24
+ function useTextCycler(text: string, isActive: boolean, intervalMs = 2400) {
25
+ const [displayed, setDisplayed] = useState(text || STATUS_PHRASES[0]);
26
+ const idxRef = useRef(0);
16
27
 
17
28
  useEffect(() => {
18
- if (!isActive || !text) {
29
+ if (!isActive) {
19
30
  setDisplayed("");
20
- indexRef.current = 0;
21
31
  return;
22
32
  }
23
- setDisplayed("");
24
- indexRef.current = 0;
33
+ setDisplayed(text || STATUS_PHRASES[0]);
34
+ idxRef.current = 0;
25
35
 
26
- const interval = setInterval(() => {
27
- indexRef.current += 1;
28
- if (indexRef.current <= text.length) {
29
- setDisplayed(text.slice(0, indexRef.current));
30
- } else {
31
- clearInterval(interval);
32
- }
33
- }, speed);
36
+ const timer = setInterval(() => {
37
+ idxRef.current = (idxRef.current + 1) % STATUS_PHRASES.length;
38
+ setDisplayed(STATUS_PHRASES[idxRef.current]);
39
+ }, intervalMs);
34
40
 
35
- return () => clearInterval(interval);
36
- }, [text, isActive, speed]);
41
+ return () => clearInterval(timer);
42
+ }, [text, isActive, intervalMs]);
37
43
 
38
44
  return displayed;
39
45
  }
@@ -101,10 +107,10 @@ interface AgentChipProps {
101
107
 
102
108
  function AgentChip({ agent, activity, isPopoverOpen, onTogglePopover }: AgentChipProps) {
103
109
  const isAnimating = activity.state === "thinking" || activity.state === "active";
104
- const typed = useTypewriter(
110
+ const statusText = useTextCycler(
105
111
  activity.currentAction?.text ?? "",
106
112
  isAnimating,
107
- 28
113
+ 2400
108
114
  );
109
115
 
110
116
  const chipStyle: React.CSSProperties = {
@@ -158,7 +164,7 @@ function AgentChip({ agent, activity, isPopoverOpen, onTogglePopover }: AgentChi
158
164
  </span>
159
165
  {isAnimating ? (
160
166
  <span className={s.activityText}>
161
- {typed}
167
+ {statusText}
162
168
  <span className={s.cursor} style={{ background: agent.color }} />
163
169
  </span>
164
170
  ) : activity.state === "completed" ? (
@@ -5,7 +5,7 @@
5
5
  .wrapper {
6
6
  border-radius: var(--radius-xl);
7
7
  border: 1px solid var(--color-border-subtle);
8
- background: var(--color-bg-elevated);
8
+ background: #0d1117;
9
9
  overflow: hidden;
10
10
  font-family: "SF Mono", "Fira Code", "Fira Mono", Menlo, Consolas, monospace;
11
11
  }
@@ -17,8 +17,8 @@
17
17
  align-items: center;
18
18
  justify-content: space-between;
19
19
  padding: 10px 16px;
20
- border-bottom: 1px solid var(--color-border-subtle);
21
- background: var(--color-bg-card);
20
+ border-bottom: 1px solid rgba(255,255,255,0.06);
21
+ background: #0a0e14;
22
22
  }
23
23
 
24
24
  .headerLeft {
@@ -77,7 +77,7 @@
77
77
  overflow-x: auto;
78
78
  font-size: 13px;
79
79
  line-height: 1.65;
80
- color: var(--color-text-secondary);
80
+ color: #c9d1d9;
81
81
  }
82
82
 
83
83
  .table {
@@ -95,8 +95,8 @@
95
95
  text-align: right;
96
96
  padding-right: 16px;
97
97
  user-select: none;
98
- color: var(--color-text-tertiary);
99
- opacity: 0.5;
98
+ color: #484f58;
99
+ opacity: 1;
100
100
  font-size: 12px;
101
101
  min-width: 32px;
102
102
  vertical-align: top;
@@ -116,43 +116,63 @@
116
116
  padding: 0;
117
117
  }
118
118
 
119
- /* ── Syntax colors ── */
119
+ /* ── Syntax colors (GitHub Dark-inspired) ── */
120
120
 
121
121
  .tokenKeyword {
122
- color: var(--color-lime);
123
- font-weight: 600;
122
+ color: #ff7b72;
123
+ font-weight: 500;
124
124
  }
125
125
 
126
126
  .tokenString {
127
- color: var(--color-info);
127
+ color: #a5d6ff;
128
128
  }
129
129
 
130
130
  .tokenComment {
131
- color: var(--color-text-tertiary);
131
+ color: #8b949e;
132
132
  font-style: italic;
133
- opacity: 0.7;
134
133
  }
135
134
 
136
135
  .tokenFunction {
137
- color: var(--color-warning);
136
+ color: #d2a8ff;
138
137
  }
139
138
 
140
139
  .tokenNumber {
141
- color: var(--color-error);
140
+ color: #79c0ff;
142
141
  }
143
142
 
144
143
  .tokenPunctuation {
145
- color: var(--color-text-tertiary);
144
+ color: #8b949e;
146
145
  }
147
146
 
148
147
  .tokenTag {
149
- color: var(--color-error);
148
+ color: #7ee787;
150
149
  }
151
150
 
152
151
  .tokenAttr {
153
- color: var(--color-lime);
152
+ color: #79c0ff;
154
153
  }
155
154
 
156
155
  .tokenAttrValue {
157
- color: var(--color-info);
156
+ color: #a5d6ff;
157
+ }
158
+
159
+ .tokenType {
160
+ color: #ffa657;
161
+ }
162
+
163
+ .tokenOperator {
164
+ color: #ff7b72;
165
+ }
166
+
167
+ .tokenProperty {
168
+ color: #79c0ff;
169
+ }
170
+
171
+ .tokenConst {
172
+ color: #79c0ff;
173
+ font-weight: 500;
174
+ }
175
+
176
+ .tokenBool {
177
+ color: #79c0ff;
158
178
  }
@@ -26,6 +26,86 @@ export interface CodeSnippetProps {
26
26
  className?: string;
27
27
  }
28
28
 
29
+ /* ══════════════════════════════════════════════
30
+ Simple syntax tokenizer
31
+ ══════════════════════════════════════════════ */
32
+
33
+ interface Token {
34
+ type: string;
35
+ value: string;
36
+ }
37
+
38
+ const KEYWORDS = new Set([
39
+ "import", "export", "from", "const", "let", "var", "function", "return",
40
+ "if", "else", "for", "while", "do", "switch", "case", "break", "continue",
41
+ "class", "extends", "new", "this", "super", "typeof", "instanceof",
42
+ "try", "catch", "finally", "throw", "async", "await", "yield",
43
+ "default", "interface", "type", "enum", "implements", "package",
44
+ "private", "protected", "public", "static", "void", "null", "undefined",
45
+ "def", "print", "elif", "except", "raise", "with", "as", "pass", "lambda",
46
+ "in", "not", "and", "or", "is", "None", "self", "cls",
47
+ ]);
48
+
49
+ const TYPES = new Set([
50
+ "string", "number", "boolean", "any", "object", "Array", "Promise",
51
+ "Record", "Map", "Set", "React", "HTMLElement", "void",
52
+ "int", "float", "str", "list", "dict", "tuple", "bool",
53
+ ]);
54
+
55
+ const BOOLEANS = new Set(["true", "false", "True", "False"]);
56
+
57
+ function tokenizeLine(line: string): Token[] {
58
+ const tokens: Token[] = [];
59
+ const regex = /(\/\/.*$|\/\*[\s\S]*?\*\/|#.*$)|("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|(\b\d+\.?\d*\b)|(=>|===?|!==?|<=?|>=?|&&|\|\||[+\-*/%]=?|\.\.\.)|(\b[A-Z][a-zA-Z0-9]*\b)|(\b[a-zA-Z_$][a-zA-Z0-9_$]*(?=\s*\())|(\b[a-zA-Z_$][a-zA-Z0-9_$]*\b)|([{}()\[\];:,.])|([^\S]+)|(.)/g;
60
+ let match;
61
+ while ((match = regex.exec(line)) !== null) {
62
+ const [full, comment, str, num, op, pascalCase, fn, word, punct, ws, other] = match;
63
+ if (comment) tokens.push({ type: "comment", value: comment });
64
+ else if (str) tokens.push({ type: "string", value: str });
65
+ else if (num) tokens.push({ type: "number", value: num });
66
+ else if (op) tokens.push({ type: "operator", value: op });
67
+ else if (pascalCase) {
68
+ if (TYPES.has(pascalCase)) tokens.push({ type: "type", value: pascalCase });
69
+ else tokens.push({ type: "type", value: pascalCase });
70
+ }
71
+ else if (fn) {
72
+ if (KEYWORDS.has(fn)) tokens.push({ type: "keyword", value: fn });
73
+ else tokens.push({ type: "function", value: fn });
74
+ }
75
+ else if (word) {
76
+ if (KEYWORDS.has(word)) tokens.push({ type: "keyword", value: word });
77
+ else if (BOOLEANS.has(word)) tokens.push({ type: "bool", value: word });
78
+ else if (TYPES.has(word)) tokens.push({ type: "type", value: word });
79
+ else tokens.push({ type: "plain", value: word });
80
+ }
81
+ else if (punct) tokens.push({ type: "punctuation", value: punct });
82
+ else tokens.push({ type: "plain", value: full });
83
+ }
84
+ return tokens;
85
+ }
86
+
87
+ const TOKEN_CLASS_MAP: Record<string, string> = {
88
+ keyword: s.tokenKeyword,
89
+ string: s.tokenString,
90
+ comment: s.tokenComment,
91
+ function: s.tokenFunction,
92
+ number: s.tokenNumber,
93
+ punctuation: s.tokenPunctuation,
94
+ type: s.tokenType,
95
+ operator: s.tokenOperator,
96
+ bool: s.tokenBool,
97
+ };
98
+
99
+ function renderTokenizedLine(line: string, lineIdx: number) {
100
+ if (!line) return "\n";
101
+ const tokens = tokenizeLine(line);
102
+ return tokens.map((tok, i) => {
103
+ const cls = TOKEN_CLASS_MAP[tok.type];
104
+ if (cls) return <span key={`${lineIdx}-${i}`} className={cls}>{tok.value}</span>;
105
+ return <React.Fragment key={`${lineIdx}-${i}`}>{tok.value}</React.Fragment>;
106
+ });
107
+ }
108
+
29
109
  /* ══════════════════════════════════════════════
30
110
  CodeSnippet
31
111
  ══════════════════════════════════════════════ */
@@ -92,13 +172,15 @@ export function CodeSnippet({
92
172
  {i + 1}
93
173
  </span>
94
174
  <span className={s.lineContent} role="cell">
95
- {line || "\n"}
175
+ {renderTokenizedLine(line, i)}
96
176
  </span>
97
177
  </div>
98
178
  ))}
99
179
  </div>
100
180
  ) : (
101
- <pre className={s.codeBlock}>{code}</pre>
181
+ <pre className={s.codeBlock}>{lines.map((line, i) => (
182
+ <React.Fragment key={i}>{renderTokenizedLine(line, i)}{"\n"}</React.Fragment>
183
+ ))}</pre>
102
184
  )}
103
185
  </div>
104
186
  </div>