@miozu/jera 0.3.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CLAUDE.md +350 -59
  2. package/README.md +30 -22
  3. package/llms.txt +37 -4
  4. package/package.json +12 -2
  5. package/src/components/docs/CodeBlock.svelte +203 -0
  6. package/src/components/docs/DocSection.svelte +120 -0
  7. package/src/components/docs/PropsTable.svelte +136 -0
  8. package/src/components/docs/SplitPane.svelte +98 -0
  9. package/src/components/docs/index.js +14 -0
  10. package/src/components/feedback/Alert.svelte +234 -0
  11. package/src/components/feedback/EmptyState.svelte +6 -6
  12. package/src/components/feedback/ProgressBar.svelte +8 -8
  13. package/src/components/feedback/Skeleton.svelte +4 -4
  14. package/src/components/feedback/Spinner.svelte +1 -1
  15. package/src/components/feedback/Toast.svelte +137 -173
  16. package/src/components/forms/Checkbox.svelte +10 -10
  17. package/src/components/forms/Dropzone.svelte +14 -14
  18. package/src/components/forms/FileUpload.svelte +16 -16
  19. package/src/components/forms/IconInput.svelte +4 -4
  20. package/src/components/forms/Input.svelte +14 -14
  21. package/src/components/forms/NumberInput.svelte +13 -13
  22. package/src/components/forms/PinInput.svelte +8 -8
  23. package/src/components/forms/Radio.svelte +8 -8
  24. package/src/components/forms/RangeSlider.svelte +12 -12
  25. package/src/components/forms/SearchInput.svelte +10 -10
  26. package/src/components/forms/Select.svelte +156 -158
  27. package/src/components/forms/Switch.svelte +4 -4
  28. package/src/components/forms/Textarea.svelte +9 -9
  29. package/src/components/navigation/Accordion.svelte +1 -1
  30. package/src/components/navigation/AccordionItem.svelte +6 -6
  31. package/src/components/navigation/NavigationContainer.svelte +344 -0
  32. package/src/components/navigation/Sidebar.svelte +334 -0
  33. package/src/components/navigation/SidebarAccountGroup.svelte +495 -0
  34. package/src/components/navigation/SidebarAccountItem.svelte +492 -0
  35. package/src/components/navigation/SidebarGroup.svelte +230 -0
  36. package/src/components/navigation/SidebarGroupSwitcher.svelte +262 -0
  37. package/src/components/navigation/SidebarItem.svelte +210 -0
  38. package/src/components/navigation/SidebarNavigationItem.svelte +470 -0
  39. package/src/components/navigation/SidebarPopover.svelte +145 -0
  40. package/src/components/navigation/SidebarSearch.svelte +236 -0
  41. package/src/components/navigation/SidebarSection.svelte +158 -0
  42. package/src/components/navigation/SidebarToggle.svelte +86 -0
  43. package/src/components/navigation/Tabs.svelte +18 -18
  44. package/src/components/navigation/WorkspaceMenu.svelte +416 -0
  45. package/src/components/navigation/blocks/NavigationAccountGroup.svelte +396 -0
  46. package/src/components/navigation/blocks/NavigationCustomBlock.svelte +74 -0
  47. package/src/components/navigation/blocks/NavigationGroupSwitcher.svelte +277 -0
  48. package/src/components/navigation/blocks/NavigationSearch.svelte +300 -0
  49. package/src/components/navigation/blocks/NavigationSection.svelte +230 -0
  50. package/src/components/navigation/index.js +22 -0
  51. package/src/components/overlays/ConfirmDialog.svelte +18 -18
  52. package/src/components/overlays/Dropdown.svelte +2 -2
  53. package/src/components/overlays/DropdownDivider.svelte +1 -1
  54. package/src/components/overlays/DropdownItem.svelte +5 -5
  55. package/src/components/overlays/Modal.svelte +13 -13
  56. package/src/components/overlays/Popover.svelte +3 -3
  57. package/src/components/primitives/Avatar.svelte +12 -12
  58. package/src/components/primitives/Badge.svelte +7 -7
  59. package/src/components/primitives/Button.svelte +126 -174
  60. package/src/components/primitives/Card.svelte +15 -15
  61. package/src/components/primitives/Divider.svelte +3 -3
  62. package/src/components/primitives/LazyImage.svelte +1 -1
  63. package/src/components/primitives/Link.svelte +2 -2
  64. package/src/components/primitives/Stat.svelte +197 -0
  65. package/src/components/primitives/StatusBadge.svelte +24 -24
  66. package/src/index.js +62 -7
  67. package/src/tokens/colors.css +96 -128
  68. package/src/utils/highlighter.js +124 -0
  69. package/src/utils/index.js +7 -2
  70. package/src/utils/navigation.svelte.js +423 -0
  71. package/src/utils/reactive.svelte.js +126 -37
  72. package/src/utils/sidebar.svelte.js +211 -0
@@ -0,0 +1,236 @@
1
+ <!--
2
+ @component SidebarSearch
3
+
4
+ Search functionality for sidebar with expand/collapse behavior.
5
+ Matches dash.selify.ai search with filtering capabilities.
6
+
7
+ @example
8
+ <SidebarSearch
9
+ bind:query={searchQuery}
10
+ bind:expanded={searchExpanded}
11
+ placeholder="Search navigation..."
12
+ onSearch={(query) => filterNavigation(query)}
13
+ />
14
+ -->
15
+ <script>
16
+ import { getContext } from 'svelte';
17
+ import { fade, fly } from 'svelte/transition';
18
+ import { SIDEBAR_CONTEXT_KEY } from '../../utils/sidebar.svelte.js';
19
+
20
+ let {
21
+ query = $bindable(''),
22
+ expanded = $bindable(false),
23
+ placeholder = 'Search...',
24
+ onSearch = null,
25
+ onClear = null,
26
+ class: className = ''
27
+ } = $props();
28
+
29
+ const sidebar = getContext(SIDEBAR_CONTEXT_KEY);
30
+ const isCollapsed = $derived(sidebar?.collapsed ?? false);
31
+
32
+ let searchInput;
33
+
34
+ function expandSearch() {
35
+ expanded = true;
36
+ // Focus input after transition
37
+ setTimeout(() => {
38
+ if (searchInput) searchInput.focus();
39
+ }, 50);
40
+ }
41
+
42
+ function collapseSearch() {
43
+ expanded = false;
44
+ query = '';
45
+ if (onClear) onClear();
46
+ }
47
+
48
+ function handleInput(event) {
49
+ query = event.target.value;
50
+ if (onSearch) onSearch(query);
51
+ }
52
+
53
+ function handleKeydown(event) {
54
+ if (event.key === 'Escape') {
55
+ collapseSearch();
56
+ }
57
+ }
58
+
59
+ function handleBlur() {
60
+ // Collapse if query is empty after a delay
61
+ setTimeout(() => {
62
+ if (!query.trim()) {
63
+ expanded = false;
64
+ }
65
+ }, 200);
66
+ }
67
+ </script>
68
+
69
+ <div class="search-section {className}">
70
+ {#if expanded}
71
+ <div class="search-input-wrapper" transition:fly={{ x: -20, duration: 200 }}>
72
+ <svg
73
+ class="search-icon"
74
+ xmlns="http://www.w3.org/2000/svg"
75
+ width="16"
76
+ height="16"
77
+ viewBox="0 0 24 24"
78
+ fill="none"
79
+ stroke="currentColor"
80
+ stroke-width="2"
81
+ stroke-linecap="round"
82
+ stroke-linejoin="round"
83
+ >
84
+ <circle cx="11" cy="11" r="8"></circle>
85
+ <path d="m21 21-4.35-4.35"></path>
86
+ </svg>
87
+ <input
88
+ bind:this={searchInput}
89
+ type="text"
90
+ bind:value={query}
91
+ {placeholder}
92
+ class="search-input"
93
+ oninput={handleInput}
94
+ onblur={handleBlur}
95
+ onkeydown={handleKeydown}
96
+ />
97
+ <button
98
+ class="search-close"
99
+ onclick={collapseSearch}
100
+ title="Clear search"
101
+ >
102
+ <svg
103
+ xmlns="http://www.w3.org/2000/svg"
104
+ width="14"
105
+ height="14"
106
+ viewBox="0 0 24 24"
107
+ fill="none"
108
+ stroke="currentColor"
109
+ stroke-width="2"
110
+ stroke-linecap="round"
111
+ stroke-linejoin="round"
112
+ >
113
+ <line x1="18" y1="6" x2="6" y2="18"></line>
114
+ <line x1="6" y1="6" x2="18" y2="18"></line>
115
+ </svg>
116
+ </button>
117
+ </div>
118
+ {:else}
119
+ <button
120
+ class="search-toggle"
121
+ onclick={expandSearch}
122
+ title="Search navigation"
123
+ transition:fly={{ x: -20, duration: 200, delay: 50 }}
124
+ >
125
+ <svg
126
+ xmlns="http://www.w3.org/2000/svg"
127
+ width="16"
128
+ height="16"
129
+ viewBox="0 0 24 24"
130
+ fill="none"
131
+ stroke="currentColor"
132
+ stroke-width="2"
133
+ stroke-linecap="round"
134
+ stroke-linejoin="round"
135
+ >
136
+ <circle cx="11" cy="11" r="8"></circle>
137
+ <path d="m21 21-4.35-4.35"></path>
138
+ </svg>
139
+ {#if !isCollapsed}
140
+ <span class="search-label" transition:fly={{ x: -15, duration: 200, delay: 75 }}>
141
+ Search
142
+ </span>
143
+ {/if}
144
+ </button>
145
+ {/if}
146
+ </div>
147
+
148
+ <style>
149
+ .search-section {
150
+ padding: 0.5rem;
151
+ border-bottom: 1px solid color-mix(in srgb, var(--color-base03, #565E78) 30%, transparent);
152
+ }
153
+
154
+ .search-toggle {
155
+ width: 100%;
156
+ display: flex;
157
+ align-items: center;
158
+ gap: 0.75rem;
159
+ padding: 0.5rem 0.75rem;
160
+ font-size: 0.875rem;
161
+ color: var(--color-text, var(--color-base06, #F3F4F7));
162
+ cursor: pointer;
163
+ border-radius: 0.375rem;
164
+ transition: all 200ms ease;
165
+ border: none;
166
+ background: transparent;
167
+ font-family: inherit;
168
+ text-align: left;
169
+ }
170
+
171
+ .search-toggle:hover {
172
+ color: var(--color-primary, var(--color-base0D, #83D2FC));
173
+ background-color: color-mix(in srgb, var(--color-primary, var(--color-base0D, #83D2FC)) 10%, transparent);
174
+ }
175
+
176
+ .search-label {
177
+ flex: 1;
178
+ text-align: left;
179
+ font-weight: 500;
180
+ }
181
+
182
+ .search-input-wrapper {
183
+ position: relative;
184
+ display: flex;
185
+ align-items: center;
186
+ padding: 0 0.75rem;
187
+ }
188
+
189
+ .search-icon {
190
+ position: absolute;
191
+ left: 1.25rem;
192
+ color: var(--color-text-muted, var(--color-base04, #737E99));
193
+ pointer-events: none;
194
+ z-index: 10;
195
+ }
196
+
197
+ .search-input {
198
+ width: 100%;
199
+ padding: 0.5rem 0.5rem 0.5rem 2.25rem;
200
+ background-color: var(--color-surface-alt, var(--color-base02, #3E4359));
201
+ border: 1px solid color-mix(in srgb, var(--color-base03, #565E78) 40%, transparent);
202
+ border-radius: 0.375rem;
203
+ font-size: 0.875rem;
204
+ color: var(--color-text, var(--color-base06, #F3F4F7));
205
+ transition: all 200ms ease;
206
+ font-family: inherit;
207
+ }
208
+
209
+ .search-input::placeholder {
210
+ color: var(--color-text-muted, var(--color-base04, #737E99));
211
+ }
212
+
213
+ .search-input:focus {
214
+ outline: none;
215
+ border-color: var(--color-primary, var(--color-base0D, #83D2FC));
216
+ background-color: var(--color-surface, var(--color-base01, #2C3040));
217
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary, var(--color-base0D, #83D2FC)) 20%, transparent);
218
+ }
219
+
220
+ .search-close {
221
+ position: absolute;
222
+ right: 1.25rem;
223
+ padding: 0.25rem;
224
+ border-radius: 0.25rem;
225
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
226
+ cursor: pointer;
227
+ border: none;
228
+ background: transparent;
229
+ transition: all 200ms ease;
230
+ }
231
+
232
+ .search-close:hover {
233
+ background-color: color-mix(in srgb, var(--color-base03, #565E78) 50%, transparent);
234
+ color: var(--color-text, var(--color-base06, #F3F4F7));
235
+ }
236
+ </style>
@@ -0,0 +1,158 @@
1
+ <!--
2
+ @component SidebarSection
3
+
4
+ Grouped section within sidebar with optional title.
5
+ Shows title when expanded, shows divider when collapsed.
6
+
7
+ @example
8
+ <SidebarSection title="Management">
9
+ <SidebarItem href="/settings" icon={Settings} label="Settings" />
10
+ <SidebarItem href="/billing" icon={CreditCard} label="Billing" />
11
+ </SidebarSection>
12
+ -->
13
+ <script>
14
+ import { getContext } from 'svelte';
15
+ import { fade, fly, slide } from 'svelte/transition';
16
+ import { cubicOut } from 'svelte/easing';
17
+ import { SIDEBAR_CONTEXT_KEY } from '../../utils/sidebar.svelte.js';
18
+
19
+ let {
20
+ title = '',
21
+ count = null,
22
+ expandable = false,
23
+ expanded = $bindable(true),
24
+ class: className = '',
25
+ children
26
+ } = $props();
27
+
28
+ const sidebar = getContext(SIDEBAR_CONTEXT_KEY);
29
+ const isCollapsed = $derived(sidebar?.collapsed ?? false);
30
+
31
+ function toggleExpanded() {
32
+ if (expandable) {
33
+ expanded = !expanded;
34
+ }
35
+ }
36
+ </script>
37
+
38
+ <div class="sidebar-section {className}">
39
+ {#if title}
40
+ {#if !isCollapsed}
41
+ {#if expandable}
42
+ <button
43
+ class="section-header expandable"
44
+ onclick={toggleExpanded}
45
+ aria-expanded={expanded}
46
+ transition:fade={{ duration: 150 }}
47
+ >
48
+ <span class="section-title">{title}</span>
49
+ {#if count !== null}
50
+ <span class="section-count">{count}</span>
51
+ {/if}
52
+ <svg
53
+ class="section-chevron {expanded ? 'rotate-180' : ''}"
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ width="14"
56
+ height="14"
57
+ viewBox="0 0 24 24"
58
+ fill="none"
59
+ stroke="currentColor"
60
+ stroke-width="2"
61
+ stroke-linecap="round"
62
+ stroke-linejoin="round"
63
+ >
64
+ <polyline points="6 9 12 15 18 9"></polyline>
65
+ </svg>
66
+ </button>
67
+ {:else}
68
+ <div class="section-header" transition:fade={{ duration: 150 }}>
69
+ <span class="section-title">{title}</span>
70
+ {#if count !== null}
71
+ <span class="section-count">{count}</span>
72
+ {/if}
73
+ </div>
74
+ {/if}
75
+ {:else}
76
+ <div class="section-divider"></div>
77
+ {/if}
78
+ {/if}
79
+
80
+ {#if !expandable || expanded}
81
+ <ul class="nav-list" transition:slide={{ duration: 200, easing: cubicOut }}>
82
+ {@render children?.()}
83
+ </ul>
84
+ {/if}
85
+ </div>
86
+
87
+ <style>
88
+ .sidebar-section {
89
+ padding: 0;
90
+ }
91
+
92
+ .section-header {
93
+ padding: 0.25rem 0.75rem;
94
+ margin-bottom: 0.25rem;
95
+ font-size: 0.75rem;
96
+ font-weight: 600;
97
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
98
+ text-align: left;
99
+ text-transform: uppercase;
100
+ letter-spacing: 0.05em;
101
+ white-space: nowrap;
102
+ overflow: hidden;
103
+ display: flex;
104
+ align-items: center;
105
+ gap: 0.5rem;
106
+ }
107
+
108
+ .section-header.expandable {
109
+ cursor: pointer;
110
+ transition: all 200ms ease;
111
+ border: none;
112
+ background: transparent;
113
+ font-family: inherit;
114
+ width: 100%;
115
+ justify-content: flex-start;
116
+ }
117
+
118
+ .section-header.expandable:hover {
119
+ color: var(--color-text, var(--color-base06, #F3F4F7));
120
+ }
121
+
122
+ .section-title {
123
+ flex: 1;
124
+ text-align: left;
125
+ }
126
+
127
+ .section-count {
128
+ padding: 0.125rem 0.375rem;
129
+ font-size: 0.625rem;
130
+ background-color: var(--color-surface-alt, var(--color-base02, #3E4359));
131
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
132
+ border-radius: 9999px;
133
+ flex-shrink: 0;
134
+ }
135
+
136
+ .section-chevron {
137
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
138
+ transition: all 200ms ease;
139
+ flex-shrink: 0;
140
+ }
141
+
142
+ .section-chevron.rotate-180 {
143
+ transform: rotate(180deg);
144
+ }
145
+
146
+ .section-divider {
147
+ margin: 0.5rem 0.75rem;
148
+ border-bottom: 1px solid color-mix(in srgb, var(--color-base03, #565E78) 30%, transparent);
149
+ }
150
+
151
+ .nav-list {
152
+ display: flex;
153
+ flex-direction: column;
154
+ list-style: none;
155
+ margin: 0;
156
+ padding: 0;
157
+ }
158
+ </style>
@@ -0,0 +1,86 @@
1
+ <!--
2
+ @component SidebarToggle
3
+
4
+ Toggle button for collapsing/expanding the sidebar.
5
+ Automatically gets collapsed state from Sidebar context.
6
+
7
+ @example
8
+ <Sidebar bind:collapsed>
9
+ ...
10
+ {#snippet footer()}
11
+ <SidebarToggle />
12
+ {/snippet}
13
+ </Sidebar>
14
+
15
+ @example With custom styling
16
+ <SidebarToggle class="my-custom-toggle" />
17
+ -->
18
+ <script>
19
+ import { getContext } from 'svelte';
20
+ import { SIDEBAR_CONTEXT_KEY } from '../../utils/sidebar.svelte.js';
21
+
22
+ let {
23
+ class: className = ''
24
+ } = $props();
25
+
26
+ const sidebar = getContext(SIDEBAR_CONTEXT_KEY);
27
+ const isCollapsed = $derived(sidebar?.collapsed ?? false);
28
+
29
+ function handleClick() {
30
+ sidebar?.toggle?.();
31
+ }
32
+ </script>
33
+
34
+ <button
35
+ type="button"
36
+ class="sidebar-toggle {className}"
37
+ onclick={handleClick}
38
+ title={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
39
+ aria-label={isCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
40
+ aria-expanded={!isCollapsed}
41
+ >
42
+ <svg
43
+ class="toggle-icon"
44
+ class:collapsed={isCollapsed}
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ width="16"
47
+ height="16"
48
+ viewBox="0 0 24 24"
49
+ fill="none"
50
+ stroke="currentColor"
51
+ stroke-width="2"
52
+ stroke-linecap="round"
53
+ stroke-linejoin="round"
54
+ >
55
+ <path d="m15 18-6-6 6-6"></path>
56
+ </svg>
57
+ </button>
58
+
59
+ <style>
60
+ .sidebar-toggle {
61
+ width: 100%;
62
+ padding: 0.625rem;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
67
+ background: transparent;
68
+ border: none;
69
+ border-radius: 0.5rem;
70
+ cursor: pointer;
71
+ transition: all 150ms ease;
72
+ }
73
+
74
+ .sidebar-toggle:hover {
75
+ color: var(--color-text, var(--color-base06, #F3F4F7));
76
+ background-color: var(--color-surface-alt, var(--color-base02, #3E4359));
77
+ }
78
+
79
+ .toggle-icon {
80
+ transition: transform 200ms ease;
81
+ }
82
+
83
+ .toggle-icon.collapsed {
84
+ transform: rotate(180deg);
85
+ }
86
+ </style>
@@ -108,7 +108,7 @@
108
108
  .tabs {
109
109
  display: inline-flex;
110
110
  gap: 0.25rem;
111
- background: var(--color-surface);
111
+ background: var(--color-base01);
112
112
  border-radius: 0.5rem;
113
113
  padding: 0.25rem;
114
114
  }
@@ -133,7 +133,7 @@
133
133
  border-radius: 0.375rem;
134
134
  font-size: 0.875rem;
135
135
  font-weight: 500;
136
- color: var(--color-text);
136
+ color: var(--color-base05);
137
137
  cursor: pointer;
138
138
  transition: all 0.15s ease;
139
139
  white-space: nowrap;
@@ -151,18 +151,18 @@
151
151
  }
152
152
 
153
153
  .tab:hover:not(.tab-disabled) {
154
- color: var(--color-text-strong);
155
- background: var(--color-surface-alt);
154
+ color: var(--color-base07);
155
+ background: var(--color-base02);
156
156
  }
157
157
 
158
158
  .tab:focus-visible {
159
159
  outline: none;
160
- box-shadow: 0 0 0 2px var(--color-primary);
160
+ box-shadow: 0 0 0 2px var(--color-base0D);
161
161
  }
162
162
 
163
163
  .tab-active {
164
- background: var(--color-surface-alt);
165
- color: var(--color-text-strong);
164
+ background: var(--color-base02);
165
+ color: var(--color-base07);
166
166
  box-shadow: var(--shadow-sm);
167
167
  }
168
168
 
@@ -183,15 +183,15 @@
183
183
  min-width: 1.25rem;
184
184
  height: 1.25rem;
185
185
  padding: 0 0.375rem;
186
- background: var(--color-border);
186
+ background: var(--color-base03);
187
187
  border-radius: 9999px;
188
188
  font-size: 0.75rem;
189
189
  font-weight: 600;
190
190
  }
191
191
 
192
192
  .tab-active .tab-badge {
193
- background: var(--color-primary);
194
- color: var(--color-text-strong);
193
+ background: var(--color-base0D);
194
+ color: var(--color-base07);
195
195
  }
196
196
 
197
197
  /* Underline variant */
@@ -199,7 +199,7 @@
199
199
  background: transparent;
200
200
  padding: 0;
201
201
  gap: 0;
202
- border-bottom: 1px solid var(--color-border);
202
+ border-bottom: 1px solid var(--color-base03);
203
203
  }
204
204
 
205
205
  .tabs-underline .tab {
@@ -211,13 +211,13 @@
211
211
  .tabs-underline .tab-active {
212
212
  background: transparent;
213
213
  box-shadow: none;
214
- border-bottom-color: var(--color-primary);
215
- color: var(--color-primary);
214
+ border-bottom-color: var(--color-base0D);
215
+ color: var(--color-base0D);
216
216
  }
217
217
 
218
218
  .tabs-underline .tab:hover:not(.tab-disabled) {
219
219
  background: transparent;
220
- border-bottom-color: var(--color-border);
220
+ border-bottom-color: var(--color-base03);
221
221
  }
222
222
 
223
223
  /* Pills variant */
@@ -228,12 +228,12 @@
228
228
  }
229
229
 
230
230
  .tabs-pills .tab {
231
- border: 1px solid var(--color-border);
231
+ border: 1px solid var(--color-base03);
232
232
  }
233
233
 
234
234
  .tabs-pills .tab-active {
235
- background: var(--color-primary);
236
- border-color: var(--color-primary);
237
- color: var(--color-text-strong);
235
+ background: var(--color-base0D);
236
+ border-color: var(--color-base0D);
237
+ color: var(--color-base07);
238
238
  }
239
239
  </style>