@shohojdhara/atomix 0.3.5 → 0.3.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.
Files changed (173) hide show
  1. package/README.md +101 -199
  2. package/atomix.config.ts +241 -0
  3. package/dist/atomix.css +260 -179
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +250 -179
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/charts.js +61 -66
  8. package/dist/charts.js.map +1 -1
  9. package/dist/core.js +47 -31
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js +47 -31
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +47 -31
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +1841 -1633
  16. package/dist/index.esm.js +4975 -4113
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +5151 -4290
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/dist/theme.d.ts +1572 -1442
  23. package/dist/theme.js +4816 -4080
  24. package/dist/theme.js.map +1 -1
  25. package/package.json +6 -20
  26. package/src/components/Accordion/Accordion.stories.tsx +50 -17
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +65 -31
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +11 -4
  29. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +1 -32
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2 -2
  31. package/src/components/AtomixGlass/stories/shared-components.tsx +0 -31
  32. package/src/components/Avatar/Avatar.stories.tsx +7 -0
  33. package/src/components/Badge/Badge.stories.tsx +91 -13
  34. package/src/components/Block/Block.stories.tsx +7 -23
  35. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +7 -0
  36. package/src/components/Button/Button.stories.tsx +141 -22
  37. package/src/components/Button/ButtonGroup.stories.tsx +315 -0
  38. package/src/components/Button/ButtonGroup.tsx +67 -0
  39. package/src/components/Button/index.ts +2 -0
  40. package/src/components/Callout/Callout.stories.tsx +8 -6
  41. package/src/components/Card/Card.stories.tsx +82 -28
  42. package/src/components/Chart/AnimatedChart.tsx +0 -1
  43. package/src/components/Chart/AreaChart.tsx +0 -1
  44. package/src/components/Chart/BarChart.tsx +0 -1
  45. package/src/components/Chart/BubbleChart.tsx +0 -1
  46. package/src/components/Chart/CandlestickChart.tsx +0 -1
  47. package/src/components/Chart/Chart.stories.tsx +5 -7
  48. package/src/components/Chart/Chart.tsx +0 -16
  49. package/src/components/Chart/ChartRenderer.tsx +1 -1
  50. package/src/components/Chart/DonutChart.tsx +0 -1
  51. package/src/components/Chart/FunnelChart.tsx +0 -1
  52. package/src/components/Chart/GaugeChart.tsx +0 -1
  53. package/src/components/Chart/HeatmapChart.tsx +0 -1
  54. package/src/components/Chart/LineChart.tsx +0 -1
  55. package/src/components/Chart/MultiAxisChart.tsx +0 -1
  56. package/src/components/Chart/PieChart.tsx +0 -1
  57. package/src/components/Chart/RadarChart.tsx +0 -1
  58. package/src/components/Chart/ScatterChart.tsx +0 -1
  59. package/src/components/Chart/WaterfallChart.tsx +0 -1
  60. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +7 -0
  61. package/src/components/DataTable/DataTable.stories.tsx +23 -16
  62. package/src/components/DatePicker/DatePicker.stories.tsx +27 -19
  63. package/src/components/Dropdown/Dropdown.stories.tsx +11 -19
  64. package/src/components/EdgePanel/EdgePanel.stories.tsx +1 -0
  65. package/src/components/Footer/Footer.stories.tsx +8 -6
  66. package/src/components/Footer/FooterLink.tsx +9 -2
  67. package/src/components/Form/Checkbox.stories.tsx +7 -0
  68. package/src/components/Form/Form.stories.tsx +7 -0
  69. package/src/components/Form/FormGroup.stories.tsx +9 -1
  70. package/src/components/Form/Input.stories.tsx +69 -16
  71. package/src/components/Form/Radio.stories.tsx +9 -1
  72. package/src/components/Form/Select.stories.tsx +9 -1
  73. package/src/components/Form/Textarea.stories.tsx +10 -2
  74. package/src/components/Hero/Hero.stories.tsx +7 -0
  75. package/src/components/List/List.stories.tsx +7 -0
  76. package/src/components/Messages/Messages.stories.tsx +8 -7
  77. package/src/components/Modal/Modal.stories.tsx +17 -6
  78. package/src/components/Navigation/Menu/Menu.stories.tsx +7 -0
  79. package/src/components/Navigation/Nav/Nav.stories.tsx +7 -0
  80. package/src/components/Navigation/Navbar/Navbar.stories.tsx +1 -0
  81. package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +1 -1
  82. package/src/components/Pagination/Pagination.stories.tsx +188 -111
  83. package/src/components/Pagination/Pagination.tsx +83 -3
  84. package/src/components/PhotoViewer/PhotoViewer.stories.tsx +10 -5
  85. package/src/components/Popover/Popover.stories.tsx +191 -115
  86. package/src/components/ProductReview/ProductReview.stories.tsx +80 -58
  87. package/src/components/Progress/Progress.stories.tsx +79 -49
  88. package/src/components/Rating/Rating.stories.tsx +109 -84
  89. package/src/components/River/River.stories.tsx +194 -114
  90. package/src/components/SectionIntro/SectionIntro.stories.tsx +19 -9
  91. package/src/components/Slider/Slider.stories.tsx +7 -0
  92. package/src/components/Spinner/Spinner.stories.tsx +15 -11
  93. package/src/components/Steps/Steps.stories.tsx +132 -98
  94. package/src/components/Tabs/Tabs.stories.tsx +163 -112
  95. package/src/components/Testimonial/Testimonial.stories.tsx +114 -68
  96. package/src/components/Todo/Todo.stories.tsx +38 -12
  97. package/src/components/Toggle/Toggle.stories.tsx +61 -28
  98. package/src/components/Tooltip/Tooltip.stories.tsx +318 -200
  99. package/src/components/Upload/Upload.stories.tsx +122 -84
  100. package/src/components/VideoPlayer/VideoPlayer.stories.tsx +7 -24
  101. package/src/components/index.ts +1 -0
  102. package/src/lib/composables/useAtomixGlass.ts +2 -3
  103. package/src/lib/composables/useNavbar.ts +0 -10
  104. package/src/lib/config/loader.ts +2 -1
  105. package/src/lib/constants/components.ts +10 -0
  106. package/src/lib/hooks/useComponentCustomization.ts +1 -1
  107. package/src/lib/theme/README.md +174 -0
  108. package/src/lib/theme/adapters/index.ts +31 -0
  109. package/src/lib/theme/adapters/themeAdapter.ts +287 -0
  110. package/src/lib/theme/config/__tests__/configLoader.test.ts +207 -0
  111. package/src/lib/theme/config/configLoader.ts +254 -0
  112. package/src/lib/theme/config/loader.ts +37 -48
  113. package/src/lib/theme/config/types.ts +2 -2
  114. package/src/lib/theme/config/validator.ts +15 -91
  115. package/src/lib/theme/{constants.ts → constants/constants.ts} +0 -18
  116. package/src/lib/theme/constants/index.ts +8 -0
  117. package/src/lib/theme/core/ThemeRegistry.ts +19 -6
  118. package/src/lib/theme/core/__tests__/createTheme.test.ts +132 -0
  119. package/src/lib/theme/core/composeTheme.ts +155 -0
  120. package/src/lib/theme/core/createTheme.ts +94 -0
  121. package/src/lib/theme/{createTheme.ts → core/createThemeObject.ts} +10 -6
  122. package/src/lib/theme/core/index.ts +5 -19
  123. package/src/lib/theme/devtools/Comparator.tsx +346 -22
  124. package/src/lib/theme/devtools/IMPROVEMENTS.md +139 -38
  125. package/src/lib/theme/devtools/Inspector.tsx +335 -51
  126. package/src/lib/theme/devtools/LiveEditor.tsx +478 -107
  127. package/src/lib/theme/devtools/Preview.tsx +471 -221
  128. package/src/lib/theme/{core → devtools}/ThemeValidator.ts +1 -1
  129. package/src/lib/theme/devtools/index.ts +14 -4
  130. package/src/lib/theme/devtools/useHistory.ts +130 -0
  131. package/src/lib/theme/errors/index.ts +12 -0
  132. package/src/lib/theme/generators/cssFile.ts +79 -0
  133. package/src/lib/theme/generators/generateCSS.ts +89 -0
  134. package/src/lib/theme/{generateCSSVariables.ts → generators/generateCSSVariables.ts} +3 -13
  135. package/src/lib/theme/generators/index.ts +19 -0
  136. package/src/lib/theme/i18n/rtl.ts +5 -6
  137. package/src/lib/theme/index.ts +120 -15
  138. package/src/lib/theme/runtime/ThemeApplicator.ts +52 -111
  139. package/src/lib/theme/{ThemeContext.tsx → runtime/ThemeContext.tsx} +1 -1
  140. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
  141. package/src/lib/theme/runtime/ThemeProvider.tsx +456 -179
  142. package/src/lib/theme/runtime/index.ts +1 -2
  143. package/src/lib/theme/runtime/useTheme.ts +1 -2
  144. package/src/lib/theme/test/testTheme.ts +385 -0
  145. package/src/lib/theme/tokens/index.ts +12 -0
  146. package/src/lib/theme/tokens/tokens.ts +721 -0
  147. package/src/lib/theme/types.ts +6 -42
  148. package/src/lib/theme/{utils.ts → utils/domUtils.ts} +2 -2
  149. package/src/lib/theme/utils/index.ts +11 -0
  150. package/src/lib/theme/utils/injectCSS.ts +90 -0
  151. package/src/lib/theme/utils/themeHelpers.ts +78 -0
  152. package/src/lib/theme/{themeUtils.ts → utils/themeUtils.ts} +1 -1
  153. package/src/lib/theme-tools.ts +7 -8
  154. package/src/lib/types/components.ts +40 -130
  155. package/src/lib/utils/componentUtils.ts +1 -1
  156. package/src/styles/01-settings/_settings.design-tokens.scss +4 -1
  157. package/src/styles/02-tools/_tools.button.scss +66 -79
  158. package/src/styles/06-components/_components.atomix-glass.scss +13 -3
  159. package/src/styles/06-components/_components.pagination.scss +88 -0
  160. package/scripts/sync-theme-config.js +0 -309
  161. package/src/lib/theme/composeTheme.ts +0 -370
  162. package/src/lib/theme/core/ThemeCache.ts +0 -283
  163. package/src/lib/theme/core/ThemeEngine.test.ts +0 -146
  164. package/src/lib/theme/core/ThemeEngine.ts +0 -665
  165. package/src/lib/theme/createThemeFromConfig.ts +0 -132
  166. package/src/lib/theme/devtools/CLI.ts +0 -364
  167. package/src/lib/theme/runtime/ThemeManager.test.ts +0 -192
  168. package/src/lib/theme/runtime/ThemeManager.ts +0 -446
  169. package/src/styles/03-generic/_generated-root.css +0 -26
  170. package/src/themes/README.md +0 -442
  171. package/src/themes/themes.config.js +0 -68
  172. /package/src/lib/theme/{cssVariableMapper.ts → adapters/cssVariableMapper.ts} +0 -0
  173. /package/src/lib/theme/{errors.ts → errors/errors.ts} +0 -0
@@ -2,9 +2,10 @@
2
2
  * Theme Comparator Component
3
3
  *
4
4
  * React component for comparing two themes side-by-side
5
+ * Enhanced with search/filter and improved visual diff styling
5
6
  */
6
7
 
7
- import React, { useMemo } from 'react';
8
+ import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react';
8
9
  import type { Theme } from '../types';
9
10
 
10
11
  /**
@@ -28,6 +29,7 @@ interface Difference {
28
29
  valueA: any;
29
30
  valueB: any;
30
31
  type: 'added' | 'removed' | 'changed';
32
+ category?: string;
31
33
  }
32
34
 
33
35
  /**
@@ -42,9 +44,37 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
42
44
  className,
43
45
  style,
44
46
  }) => {
47
+ const [searchQuery, setSearchQuery] = useState<string>('');
48
+ const [debouncedSearchQuery, setDebouncedSearchQuery] = useState<string>('');
49
+ const [filterType, setFilterType] = useState<'all' | 'added' | 'removed' | 'changed'>('all');
50
+ const [filterCategory, setFilterCategory] = useState<string>('all');
51
+ const searchTimeoutRef = useRef<NodeJS.Timeout>();
52
+
53
+ // Debounce search query
54
+ useEffect(() => {
55
+ if (searchTimeoutRef.current) {
56
+ clearTimeout(searchTimeoutRef.current);
57
+ }
58
+
59
+ searchTimeoutRef.current = setTimeout(() => {
60
+ setDebouncedSearchQuery(searchQuery);
61
+ }, 300);
62
+
63
+ return () => {
64
+ if (searchTimeoutRef.current) {
65
+ clearTimeout(searchTimeoutRef.current);
66
+ }
67
+ };
68
+ }, [searchQuery]);
69
+
45
70
  const differences = useMemo(() => {
46
71
  const diffs: Difference[] = [];
47
72
 
73
+ const getCategory = (path: string): string => {
74
+ const firstSegment = path.split('.')[0];
75
+ return firstSegment || 'other';
76
+ };
77
+
48
78
  const compareObjects = (objA: any, objB: any, path: string = '') => {
49
79
  const keysA = Object.keys(objA || {});
50
80
  const keysB = Object.keys(objB || {});
@@ -63,6 +93,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
63
93
  valueA: undefined,
64
94
  valueB,
65
95
  type: 'added',
96
+ category: getCategory(currentPath),
66
97
  });
67
98
  } else if (valueA !== undefined && valueB === undefined) {
68
99
  diffs.push({
@@ -70,6 +101,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
70
101
  valueA,
71
102
  valueB: undefined,
72
103
  type: 'removed',
104
+ category: getCategory(currentPath),
73
105
  });
74
106
  } else if (typeof valueA === 'object' && typeof valueB === 'object' && !Array.isArray(valueA) && !Array.isArray(valueB)) {
75
107
  compareObjects(valueA, valueB, currentPath);
@@ -79,6 +111,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
79
111
  valueA,
80
112
  valueB,
81
113
  type: 'changed',
114
+ category: getCategory(currentPath),
82
115
  });
83
116
  }
84
117
  }
@@ -88,6 +121,39 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
88
121
  return diffs;
89
122
  }, [themeA, themeB]);
90
123
 
124
+ // Filter differences
125
+ const filteredDifferences = useMemo(() => {
126
+ let filtered = differences;
127
+
128
+ // Filter by type
129
+ if (filterType !== 'all') {
130
+ filtered = filtered.filter(d => d.type === filterType);
131
+ }
132
+
133
+ // Filter by category
134
+ if (filterCategory !== 'all') {
135
+ filtered = filtered.filter(d => d.category === filterCategory);
136
+ }
137
+
138
+ // Filter by search query
139
+ if (debouncedSearchQuery) {
140
+ const queryLower = debouncedSearchQuery.toLowerCase();
141
+ filtered = filtered.filter(d =>
142
+ d.path.toLowerCase().includes(queryLower) ||
143
+ String(d.valueA).toLowerCase().includes(queryLower) ||
144
+ String(d.valueB).toLowerCase().includes(queryLower)
145
+ );
146
+ }
147
+
148
+ return filtered;
149
+ }, [differences, filterType, filterCategory, debouncedSearchQuery]);
150
+
151
+ // Get unique categories
152
+ const categories = useMemo(() => {
153
+ const cats = new Set(differences.map(d => d.category).filter(Boolean));
154
+ return Array.from(cats).sort();
155
+ }, [differences]);
156
+
91
157
  const formatValue = (value: any): string => {
92
158
  if (value === undefined) return 'undefined';
93
159
  if (value === null) return 'null';
@@ -105,6 +171,38 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
105
171
  }
106
172
  };
107
173
 
174
+ const getTypeBackground = (type: Difference['type']): string => {
175
+ switch (type) {
176
+ case 'added': return 'rgba(76, 175, 80, 0.1)';
177
+ case 'removed': return 'rgba(244, 67, 54, 0.1)';
178
+ case 'changed': return 'rgba(255, 152, 0, 0.1)';
179
+ default: return 'transparent';
180
+ }
181
+ };
182
+
183
+ const highlightText = (text: string, query: string): React.ReactNode => {
184
+ if (!query) return text;
185
+
186
+ const parts = text.split(new RegExp(`(${query})`, 'gi'));
187
+ return parts.map((part, index) =>
188
+ part.toLowerCase() === query.toLowerCase() ? (
189
+ <mark key={index} className="search-highlight">{part}</mark>
190
+ ) : (
191
+ part
192
+ )
193
+ );
194
+ };
195
+
196
+ const stats = useMemo(() => {
197
+ return {
198
+ total: differences.length,
199
+ added: differences.filter(d => d.type === 'added').length,
200
+ removed: differences.filter(d => d.type === 'removed').length,
201
+ changed: differences.filter(d => d.type === 'changed').length,
202
+ filtered: filteredDifferences.length,
203
+ };
204
+ }, [differences, filteredDifferences]);
205
+
108
206
  return (
109
207
  <div className={`atomix-theme-comparator ${className || ''}`} style={style}>
110
208
  <div className="comparator-header">
@@ -119,40 +217,122 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
119
217
  </div>
120
218
  </div>
121
219
 
220
+ {/* Filter Controls */}
221
+ <div className="filter-controls">
222
+ <div className="filter-group">
223
+ <label>Search:</label>
224
+ <input
225
+ type="text"
226
+ placeholder="Search differences..."
227
+ value={searchQuery}
228
+ onChange={(e) => setSearchQuery(e.target.value)}
229
+ className="search-input"
230
+ />
231
+ {searchQuery && (
232
+ <button
233
+ className="clear-search"
234
+ onClick={() => setSearchQuery('')}
235
+ title="Clear search"
236
+ >
237
+ ×
238
+ </button>
239
+ )}
240
+ </div>
241
+
242
+ <div className="filter-group">
243
+ <label>Type:</label>
244
+ <select
245
+ value={filterType}
246
+ onChange={(e) => setFilterType(e.target.value as any)}
247
+ className="filter-select"
248
+ >
249
+ <option value="all">All Types</option>
250
+ <option value="added">Added</option>
251
+ <option value="removed">Removed</option>
252
+ <option value="changed">Changed</option>
253
+ </select>
254
+ </div>
255
+
256
+ {categories.length > 0 && (
257
+ <div className="filter-group">
258
+ <label>Category:</label>
259
+ <select
260
+ value={filterCategory}
261
+ onChange={(e) => setFilterCategory(e.target.value)}
262
+ className="filter-select"
263
+ >
264
+ <option value="all">All Categories</option>
265
+ {categories.map(cat => (
266
+ <option key={cat} value={cat}>{cat}</option>
267
+ ))}
268
+ </select>
269
+ </div>
270
+ )}
271
+
272
+ {(searchQuery || filterType !== 'all' || filterCategory !== 'all') && (
273
+ <button
274
+ className="clear-filters"
275
+ onClick={() => {
276
+ setSearchQuery('');
277
+ setFilterType('all');
278
+ setFilterCategory('all');
279
+ }}
280
+ >
281
+ Clear Filters
282
+ </button>
283
+ )}
284
+ </div>
285
+
122
286
  <div className="comparator-stats">
123
287
  <div className="stat">
124
- <span className="stat-value">{differences.length}</span>
288
+ <span className="stat-value">{stats.total}</span>
125
289
  <span className="stat-label">Total Differences</span>
126
290
  </div>
127
- <div className="stat">
291
+ <div className="stat stat-added">
128
292
  <span className="stat-value" style={{ color: '#4caf50' }}>
129
- {differences.filter(d => d.type === 'added').length}
293
+ {stats.added}
130
294
  </span>
131
295
  <span className="stat-label">Added</span>
132
296
  </div>
133
- <div className="stat">
297
+ <div className="stat stat-removed">
134
298
  <span className="stat-value" style={{ color: '#f44336' }}>
135
- {differences.filter(d => d.type === 'removed').length}
299
+ {stats.removed}
136
300
  </span>
137
301
  <span className="stat-label">Removed</span>
138
302
  </div>
139
- <div className="stat">
303
+ <div className="stat stat-changed">
140
304
  <span className="stat-value" style={{ color: '#ff9800' }}>
141
- {differences.filter(d => d.type === 'changed').length}
305
+ {stats.changed}
142
306
  </span>
143
307
  <span className="stat-label">Changed</span>
144
308
  </div>
309
+ {stats.filtered !== stats.total && (
310
+ <div className="stat stat-filtered">
311
+ <span className="stat-value" style={{ color: '#2196f3' }}>
312
+ {stats.filtered}
313
+ </span>
314
+ <span className="stat-label">Filtered</span>
315
+ </div>
316
+ )}
145
317
  </div>
146
318
 
147
- {differences.length === 0 ? (
319
+ {filteredDifferences.length === 0 ? (
148
320
  <div className="no-differences">
149
- Themes are identical
321
+ {differences.length === 0 ? (
322
+ <>✅ Themes are identical</>
323
+ ) : (
324
+ <>No differences match your filters. Try adjusting your search or filters.</>
325
+ )}
150
326
  </div>
151
327
  ) : (
152
328
  <div className="differences-list">
153
- <h4>Differences</h4>
154
- {differences.map((diff, index) => (
155
- <div key={index} className="difference-item">
329
+ <h4>Differences ({filteredDifferences.length})</h4>
330
+ {filteredDifferences.map((diff, index) => (
331
+ <div
332
+ key={index}
333
+ className={`difference-item difference-${diff.type}`}
334
+ style={{ backgroundColor: getTypeBackground(diff.type) }}
335
+ >
156
336
  <div className="difference-header">
157
337
  <span
158
338
  className="difference-type"
@@ -160,20 +340,33 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
160
340
  >
161
341
  {diff.type}
162
342
  </span>
163
- <code className="difference-path">{diff.path}</code>
343
+ <code className="difference-path">
344
+ {highlightText(diff.path, debouncedSearchQuery)}
345
+ </code>
346
+ {diff.category && (
347
+ <span className="difference-category">{diff.category}</span>
348
+ )}
164
349
  </div>
165
350
  <div className="difference-values">
166
- <div className="value-column">
351
+ <div className={`value-column value-${diff.type === 'added' ? 'empty' : 'filled'}`}>
167
352
  <div className="value-label">{themeA.name}</div>
168
353
  <pre className="value-content">
169
- {formatValue(diff.valueA)}
354
+ {diff.type === 'added' ? (
355
+ <span className="value-empty">—</span>
356
+ ) : (
357
+ highlightText(formatValue(diff.valueA), debouncedSearchQuery)
358
+ )}
170
359
  </pre>
171
360
  </div>
172
361
  <div className="value-divider">→</div>
173
- <div className="value-column">
362
+ <div className={`value-column value-${diff.type === 'removed' ? 'empty' : 'filled'}`}>
174
363
  <div className="value-label">{themeB.name}</div>
175
364
  <pre className="value-content">
176
- {formatValue(diff.valueB)}
365
+ {diff.type === 'removed' ? (
366
+ <span className="value-empty">—</span>
367
+ ) : (
368
+ highlightText(formatValue(diff.valueB), debouncedSearchQuery)
369
+ )}
177
370
  </pre>
178
371
  </div>
179
372
  </div>
@@ -227,6 +420,90 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
227
420
  color: #666;
228
421
  }
229
422
 
423
+ .filter-controls {
424
+ display: flex;
425
+ gap: 16px;
426
+ align-items: center;
427
+ margin-bottom: 24px;
428
+ padding: 16px;
429
+ background: #f5f5f5;
430
+ border-radius: 8px;
431
+ flex-wrap: wrap;
432
+ }
433
+
434
+ .filter-group {
435
+ display: flex;
436
+ align-items: center;
437
+ gap: 8px;
438
+ }
439
+
440
+ .filter-group label {
441
+ font-size: 14px;
442
+ font-weight: 500;
443
+ color: #666;
444
+ }
445
+
446
+ .search-input {
447
+ padding: 8px 12px;
448
+ border: 1px solid #e0e0e0;
449
+ border-radius: 4px;
450
+ font-size: 14px;
451
+ width: 250px;
452
+ }
453
+
454
+ .search-input:focus {
455
+ outline: none;
456
+ border-color: #2196f3;
457
+ }
458
+
459
+ .clear-search {
460
+ background: #f44336;
461
+ color: white;
462
+ border: none;
463
+ border-radius: 50%;
464
+ width: 24px;
465
+ height: 24px;
466
+ cursor: pointer;
467
+ font-size: 18px;
468
+ line-height: 1;
469
+ display: flex;
470
+ align-items: center;
471
+ justify-content: center;
472
+ }
473
+
474
+ .clear-search:hover {
475
+ background: #d32f2f;
476
+ }
477
+
478
+ .filter-select {
479
+ padding: 8px 12px;
480
+ border: 1px solid #e0e0e0;
481
+ border-radius: 4px;
482
+ font-size: 14px;
483
+ background: white;
484
+ cursor: pointer;
485
+ }
486
+
487
+ .clear-filters {
488
+ padding: 8px 16px;
489
+ background: #666;
490
+ color: white;
491
+ border: none;
492
+ border-radius: 4px;
493
+ cursor: pointer;
494
+ font-size: 14px;
495
+ }
496
+
497
+ .clear-filters:hover {
498
+ background: #555;
499
+ }
500
+
501
+ .search-highlight {
502
+ background: #fff59d;
503
+ padding: 2px 4px;
504
+ border-radius: 2px;
505
+ }
506
+
230
507
  .comparator-stats {
231
508
  display: grid;
232
509
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
@@ -241,6 +518,22 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
241
518
  border-radius: 8px;
242
519
  }
243
520
 
521
+ .stat-added {
522
+ border-left: 4px solid #4caf50;
523
+ }
524
+
525
+ .stat-removed {
526
+ border-left: 4px solid #f44336;
527
+ }
528
+
529
+ .stat-changed {
530
+ border-left: 4px solid #ff9800;
531
+ }
532
+
533
+ .stat-filtered {
534
+ border-left: 4px solid #2196f3;
535
+ }
536
+
244
537
  .stat-value {
245
538
  display: block;
246
539
  font-size: 32px;
@@ -272,9 +565,22 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
272
565
 
273
566
  .difference-item {
274
567
  margin-bottom: 16px;
275
- border: 1px solid #e0e0e0;
568
+ border: 2px solid;
276
569
  border-radius: 8px;
277
570
  overflow: hidden;
571
+ transition: all 0.2s ease;
572
+ }
573
+
574
+ .difference-added {
575
+ border-color: #4caf50;
576
+ }
577
+
578
+ .difference-removed {
579
+ border-color: #f44336;
580
+ }
581
+
582
+ .difference-changed {
583
+ border-color: #ff9800;
278
584
  }
279
585
 
280
586
  .difference-header {
@@ -282,7 +588,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
282
588
  align-items: center;
283
589
  gap: 12px;
284
590
  padding: 12px;
285
- background: #f5f5f5;
591
+ background: rgba(0, 0, 0, 0.05);
286
592
  }
287
593
 
288
594
  .difference-type {
@@ -298,6 +604,16 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
298
604
  font-family: 'Monaco', 'Menlo', monospace;
299
605
  font-size: 13px;
300
606
  color: #1976d2;
607
+ flex: 1;
608
+ }
609
+
610
+ .difference-category {
611
+ font-size: 11px;
612
+ padding: 2px 6px;
613
+ background: rgba(0, 0, 0, 0.1);
614
+ border-radius: 4px;
615
+ color: #666;
616
+ text-transform: uppercase;
301
617
  }
302
618
 
303
619
  .difference-values {
@@ -312,6 +628,10 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
312
628
  min-width: 0;
313
629
  }
314
630
 
631
+ .value-column.value-empty {
632
+ opacity: 0.5;
633
+ }
634
+
315
635
  .value-label {
316
636
  font-size: 12px;
317
637
  font-weight: bold;
@@ -322,7 +642,7 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
322
642
  .value-content {
323
643
  margin: 0;
324
644
  padding: 12px;
325
- background: #f5f5f5;
645
+ background: rgba(255, 255, 255, 0.7);
326
646
  border-radius: 4px;
327
647
  font-family: 'Monaco', 'Menlo', monospace;
328
648
  font-size: 12px;
@@ -331,6 +651,11 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
331
651
  word-break: break-all;
332
652
  }
333
653
 
654
+ .value-empty {
655
+ color: #999;
656
+ font-style: italic;
657
+ }
658
+
334
659
  .value-divider {
335
660
  font-size: 20px;
336
661
  color: #666;
@@ -340,4 +665,3 @@ export const ThemeComparator: React.FC<ThemeComparatorProps> = ({
340
665
  </div>
341
666
  );
342
667
  };
343
-