@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,396 @@
1
+ <!--
2
+ @component NavigationAccountGroup
3
+
4
+ Enterprise account group block with expandable accounts and subroutes.
5
+ -->
6
+ <script>
7
+ import { getContext } from 'svelte';
8
+ import { slide, fade } from 'svelte/transition';
9
+ import { cubicOut } from 'svelte/easing';
10
+ import { SIDEBAR_CONTEXT_KEY } from '../../../utils/sidebar.svelte.js';
11
+ import { NAVIGATION_CONTEXT_KEY } from '../../../utils/navigation.svelte.js';
12
+
13
+ let {
14
+ block,
15
+ navigationState = null,
16
+ onEvent = null
17
+ } = $props();
18
+
19
+ const sidebar = getContext(SIDEBAR_CONTEXT_KEY);
20
+ const navContext = getContext(NAVIGATION_CONTEXT_KEY);
21
+ const navState = navigationState || navContext;
22
+
23
+ const isCollapsed = $derived(sidebar?.collapsed ?? false);
24
+
25
+ // Block configuration
26
+ const config = $derived({
27
+ title: block.title || 'Connected Accounts',
28
+ collapsible: block.collapsible ?? true,
29
+ defaultExpanded: block.defaultExpanded ?? true,
30
+ showCount: block.showCount ?? true,
31
+ showConnectButton: block.showConnectButton ?? true,
32
+ ...block.config
33
+ });
34
+
35
+ const accounts = $derived(block.accounts || []);
36
+ const expandedAccounts = $derived(block.expandedAccounts || {});
37
+
38
+ // Filter accounts based on search
39
+ const filteredAccounts = $derived.by(() => {
40
+ if (!navState?.searchQuery) return accounts;
41
+
42
+ const query = navState.searchQuery.toLowerCase();
43
+ return accounts.filter(account =>
44
+ account.name?.toLowerCase().includes(query) ||
45
+ account.platform?.toLowerCase().includes(query)
46
+ );
47
+ });
48
+
49
+ const sectionId = block.id;
50
+ const isExpanded = $derived(
51
+ config.collapsible
52
+ ? (navState?.isSectionExpanded(sectionId) ?? config.defaultExpanded)
53
+ : true
54
+ );
55
+
56
+ function toggleSection() {
57
+ if (navState) {
58
+ navState.toggleSection(sectionId);
59
+ }
60
+ if (onEvent) onEvent('section_toggled', { sectionId, expanded: !isExpanded });
61
+ }
62
+
63
+ function handleAccountClick(account) {
64
+ if (onEvent) {
65
+ onEvent('account_clicked', { account });
66
+ }
67
+ }
68
+
69
+ function handleSubrouteClick(account, subroute) {
70
+ if (onEvent) {
71
+ onEvent('subroute_clicked', { account, subroute });
72
+ }
73
+ }
74
+
75
+ function handleConnectAccount() {
76
+ if (onEvent) {
77
+ onEvent('connect_account_clicked', {});
78
+ }
79
+ }
80
+
81
+ function getDefaultSubroutes(account) {
82
+ return [
83
+ { id: 'overview', label: 'Overview', path: `/account/${account.id}`, icon: 'Eye' },
84
+ { id: 'chat', label: 'Chat', path: `/account/${account.id}/chat`, icon: 'MessageSquare' },
85
+ { id: 'settings', label: 'Settings', path: `/account/${account.id}/settings`, icon: 'Settings' }
86
+ ];
87
+ }
88
+ </script>
89
+
90
+ <div class="nav-account-group-block">
91
+ {#if config.title && !isCollapsed}
92
+ <button
93
+ class="section-header"
94
+ onclick={toggleSection}
95
+ aria-expanded={isExpanded}
96
+ >
97
+ <span class="section-title">{config.title}</span>
98
+
99
+ {#if config.showCount && accounts.length > 0}
100
+ <span class="section-count">{accounts.length}</span>
101
+ {/if}
102
+
103
+ <svg
104
+ class="section-chevron {isExpanded ? 'expanded' : 'collapsed'}"
105
+ xmlns="http://www.w3.org/2000/svg"
106
+ width="14"
107
+ height="14"
108
+ viewBox="0 0 24 24"
109
+ fill="none"
110
+ stroke="currentColor"
111
+ stroke-width="2"
112
+ stroke-linecap="round"
113
+ stroke-linejoin="round"
114
+ >
115
+ <polyline points="6 9 12 15 18 9"></polyline>
116
+ </svg>
117
+ </button>
118
+ {:else if config.title && isCollapsed}
119
+ <div class="section-divider"></div>
120
+ {/if}
121
+
122
+ {#if isExpanded}
123
+ <div class="accounts-list" transition:slide={{ duration: 200, easing: cubicOut }}>
124
+ {#each filteredAccounts as account (account.id)}
125
+ {@const isAccountExpanded = expandedAccounts[account.id] ?? false}
126
+ {@const subroutes = account.subroutes || getDefaultSubroutes(account)}
127
+
128
+ <div class="account-wrapper">
129
+ <!-- Main account item -->
130
+ <button
131
+ class="account-item {isAccountExpanded ? 'expanded' : ''}"
132
+ onclick={() => handleAccountClick(account)}
133
+ >
134
+ <!-- Account avatar -->
135
+ <div class="account-avatar-container">
136
+ {#if account.avatar_url}
137
+ <img src={account.avatar_url} alt={account.name} class="account-avatar-img" />
138
+ {:else}
139
+ <div class="account-avatar {account.platform}">
140
+ {account.name?.charAt(0)?.toUpperCase() || 'A'}
141
+ </div>
142
+ {/if}
143
+
144
+ <!-- Platform badge -->
145
+ <div class="platform-badge {account.platform}">
146
+ {#if account.platform === 'instagram'}
147
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
148
+ <rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
149
+ <path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
150
+ <line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
151
+ </svg>
152
+ {/if}
153
+ </div>
154
+ </div>
155
+
156
+ {#if !isCollapsed}
157
+ <div class="account-info">
158
+ <span class="account-name">{account.name}</span>
159
+ </div>
160
+
161
+ <svg
162
+ class="account-chevron {isAccountExpanded ? 'expanded' : 'collapsed'}"
163
+ xmlns="http://www.w3.org/2000/svg"
164
+ width="14"
165
+ height="14"
166
+ viewBox="0 0 24 24"
167
+ fill="none"
168
+ stroke="currentColor"
169
+ stroke-width="2"
170
+ stroke-linecap="round"
171
+ stroke-linejoin="round"
172
+ >
173
+ <polyline points="6 9 12 15 18 9"></polyline>
174
+ </svg>
175
+ {/if}
176
+ </button>
177
+
178
+ <!-- Account subroutes -->
179
+ {#if isAccountExpanded && !isCollapsed}
180
+ <div class="account-subroutes" transition:slide={{ duration: 200, easing: cubicOut }}>
181
+ {#each subroutes as subroute}
182
+ <button
183
+ class="subroute-item"
184
+ onclick={() => handleSubrouteClick(account, subroute)}
185
+ >
186
+ <span>{subroute.label}</span>
187
+ </button>
188
+ {/each}
189
+ </div>
190
+ {/if}
191
+ </div>
192
+ {/each}
193
+
194
+ <!-- Connect Account Button -->
195
+ {#if config.showConnectButton}
196
+ <button class="add-account-btn" onclick={handleConnectAccount}>
197
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
198
+ <circle cx="12" cy="12" r="10"></circle>
199
+ <line x1="12" y1="8" x2="12" y2="16"></line>
200
+ <line x1="8" y1="12" x2="16" y2="12"></line>
201
+ </svg>
202
+ <span>Connect account</span>
203
+ </button>
204
+ {/if}
205
+ </div>
206
+ {/if}
207
+ </div>
208
+
209
+ <style>
210
+ /* Use same styles as NavigationSection with account-specific additions */
211
+ .nav-account-group-block {
212
+ margin-bottom: var(--nav-block-spacing, 0.5rem);
213
+ }
214
+
215
+ .section-header {
216
+ width: 100%;
217
+ display: flex;
218
+ align-items: center;
219
+ gap: 0.5rem;
220
+ padding: 0.5rem 0.75rem;
221
+ font-size: 0.75rem;
222
+ font-weight: 600;
223
+ color: var(--color-text-muted, var(--color-base04, #737E99));
224
+ text-transform: uppercase;
225
+ letter-spacing: 0.05em;
226
+ cursor: pointer;
227
+ transition: all var(--nav-transition-duration) var(--nav-transition-easing);
228
+ border: none;
229
+ background: transparent;
230
+ font-family: inherit;
231
+ justify-content: flex-start;
232
+ }
233
+
234
+ .section-header:hover {
235
+ color: var(--color-text, var(--color-base05, #D0D2DB));
236
+ }
237
+
238
+ .section-title {
239
+ flex: 1;
240
+ text-align: left;
241
+ }
242
+
243
+ .section-count,
244
+ .section-chevron {
245
+ /* Same as NavigationSection */
246
+ }
247
+
248
+ .accounts-list {
249
+ display: flex;
250
+ flex-direction: column;
251
+ gap: 0.125rem;
252
+ padding: 0 0.5rem;
253
+ }
254
+
255
+ .account-wrapper {
256
+ display: flex;
257
+ flex-direction: column;
258
+ }
259
+
260
+ .account-item {
261
+ width: 100%;
262
+ padding: 0.5rem 0.75rem;
263
+ display: flex;
264
+ align-items: center;
265
+ gap: 0.75rem;
266
+ font-size: 0.875rem;
267
+ color: var(--nav-item-color);
268
+ cursor: pointer;
269
+ border-radius: var(--nav-item-border-radius);
270
+ transition: all var(--nav-transition-duration) var(--nav-transition-easing);
271
+ border: none;
272
+ background: transparent;
273
+ font-family: inherit;
274
+ text-align: left;
275
+ }
276
+
277
+ .account-item:hover {
278
+ color: var(--nav-item-hover-color);
279
+ background: var(--nav-item-hover-background);
280
+ }
281
+
282
+ .account-item.expanded {
283
+ background: var(--nav-item-hover-background);
284
+ color: var(--nav-item-hover-color);
285
+ }
286
+
287
+ .account-avatar-container {
288
+ position: relative;
289
+ width: 2rem;
290
+ height: 2rem;
291
+ flex-shrink: 0;
292
+ }
293
+
294
+ .account-avatar-img,
295
+ .account-avatar {
296
+ width: 2rem;
297
+ height: 2rem;
298
+ border-radius: 0.375rem;
299
+ }
300
+
301
+ .account-avatar {
302
+ display: flex;
303
+ align-items: center;
304
+ justify-content: center;
305
+ font-size: 0.75rem;
306
+ font-weight: 600;
307
+ color: white;
308
+ background: linear-gradient(to bottom right, var(--nav-item-active-color), var(--color-primary));
309
+ }
310
+
311
+ .platform-badge {
312
+ position: absolute;
313
+ bottom: -2px;
314
+ right: -2px;
315
+ width: 1rem;
316
+ height: 1rem;
317
+ border-radius: 50%;
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: center;
321
+ background: var(--color-success);
322
+ color: white;
323
+ }
324
+
325
+ .account-info {
326
+ flex: 1;
327
+ min-width: 0;
328
+ }
329
+
330
+ .account-name {
331
+ font-weight: 500;
332
+ white-space: nowrap;
333
+ overflow: hidden;
334
+ text-overflow: ellipsis;
335
+ }
336
+
337
+ .account-chevron {
338
+ transition: transform var(--nav-transition-duration) var(--nav-transition-easing);
339
+ }
340
+
341
+ .account-chevron.expanded {
342
+ transform: rotate(180deg);
343
+ }
344
+
345
+ .account-subroutes {
346
+ margin-left: 2.5rem;
347
+ padding-left: 0.5rem;
348
+ border-left: 2px solid color-mix(in srgb, var(--color-base03) 30%, transparent);
349
+ display: flex;
350
+ flex-direction: column;
351
+ gap: 0.125rem;
352
+ }
353
+
354
+ .subroute-item {
355
+ padding: 0.375rem 0.75rem;
356
+ font-size: 0.875rem;
357
+ color: var(--color-text-muted);
358
+ cursor: pointer;
359
+ border-radius: var(--nav-item-border-radius);
360
+ transition: all var(--nav-transition-duration) var(--nav-transition-easing);
361
+ border: none;
362
+ background: transparent;
363
+ font-family: inherit;
364
+ text-align: left;
365
+ width: 100%;
366
+ }
367
+
368
+ .subroute-item:hover {
369
+ color: var(--nav-item-hover-color);
370
+ background: color-mix(in srgb, var(--nav-item-hover-color) 5%, transparent);
371
+ }
372
+
373
+ .add-account-btn {
374
+ display: flex;
375
+ align-items: center;
376
+ justify-content: center;
377
+ gap: 0.5rem;
378
+ width: 100%;
379
+ padding: 0.5rem 0.75rem;
380
+ margin-top: 0.25rem;
381
+ font-size: 0.875rem;
382
+ color: var(--color-text-muted);
383
+ cursor: pointer;
384
+ border-radius: var(--nav-item-border-radius);
385
+ border: 1px solid color-mix(in srgb, var(--color-base03) 30%, transparent);
386
+ background: transparent;
387
+ font-family: inherit;
388
+ transition: all var(--nav-transition-duration) var(--nav-transition-easing);
389
+ }
390
+
391
+ .add-account-btn:hover {
392
+ color: var(--nav-item-hover-color);
393
+ background: color-mix(in srgb, var(--nav-item-hover-color) 5%, transparent);
394
+ border-color: var(--nav-item-hover-color);
395
+ }
396
+ </style>
@@ -0,0 +1,74 @@
1
+ <!--
2
+ @component NavigationCustomBlock
3
+
4
+ Custom block wrapper for user-defined components.
5
+ -->
6
+ <script>
7
+ import { getContext } from 'svelte';
8
+ import { SIDEBAR_CONTEXT_KEY } from '../../../utils/sidebar.svelte.js';
9
+ import { NAVIGATION_CONTEXT_KEY } from '../../../utils/navigation.svelte.js';
10
+
11
+ let {
12
+ block,
13
+ navigationState = null,
14
+ onEvent = null
15
+ } = $props();
16
+
17
+ const sidebar = getContext(SIDEBAR_CONTEXT_KEY);
18
+ const navContext = getContext(NAVIGATION_CONTEXT_KEY);
19
+ const navState = navigationState || navContext;
20
+
21
+ const isCollapsed = $derived(sidebar?.collapsed ?? false);
22
+
23
+ // Get the custom component
24
+ const CustomComponent = $derived(block.component);
25
+
26
+ // Prepare props for custom component
27
+ const customProps = $derived({
28
+ block,
29
+ navigationState: navState,
30
+ sidebar,
31
+ isCollapsed,
32
+ onEvent,
33
+ ...(block.props || {})
34
+ });
35
+ </script>
36
+
37
+ <div class="nav-custom-block" data-block-type={block.type} data-block-id={block.id}>
38
+ {#if CustomComponent}
39
+ <CustomComponent {...customProps} />
40
+ {:else}
41
+ <div class="custom-block-fallback">
42
+ <p class="fallback-text">Custom component not provided</p>
43
+ <p class="fallback-details">Block ID: {block.id}, Type: {block.type}</p>
44
+ </div>
45
+ {/if}
46
+ </div>
47
+
48
+ <style>
49
+ .nav-custom-block {
50
+ margin-bottom: var(--nav-block-spacing, 0.5rem);
51
+ }
52
+
53
+ .custom-block-fallback {
54
+ padding: 1rem 0.75rem;
55
+ text-align: center;
56
+ border: 1px dashed var(--color-border, color-mix(in srgb, var(--color-base03, #565E78) 30%, transparent));
57
+ border-radius: var(--nav-item-border-radius, 0.375rem);
58
+ background: var(--color-surface-alt, var(--color-base02, #3E4359));
59
+ }
60
+
61
+ .fallback-text {
62
+ font-size: 0.875rem;
63
+ color: var(--color-text-muted, var(--color-base05, #D0D2DB));
64
+ margin: 0 0 0.5rem 0;
65
+ font-weight: 500;
66
+ }
67
+
68
+ .fallback-details {
69
+ font-size: 0.75rem;
70
+ color: var(--color-text-muted, var(--color-base04, #737E99));
71
+ margin: 0;
72
+ font-family: monospace;
73
+ }
74
+ </style>