@rokkit/ui 1.0.0-next.99 → 1.0.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 (83) hide show
  1. package/README.md +159 -1
  2. package/package.json +52 -53
  3. package/src/MarkdownRenderer.svelte +106 -0
  4. package/src/components/AlertList.svelte +59 -0
  5. package/src/components/Avatar.svelte +68 -0
  6. package/src/components/Badge.svelte +57 -0
  7. package/src/components/BreadCrumbs.svelte +94 -0
  8. package/src/components/Button.svelte +91 -0
  9. package/src/components/ButtonGroup.svelte +9 -0
  10. package/src/components/Card.svelte +71 -0
  11. package/src/components/Carousel.svelte +169 -0
  12. package/src/components/Code.svelte +189 -0
  13. package/src/components/Connector.svelte +46 -0
  14. package/src/components/Divider.svelte +24 -0
  15. package/src/components/Dropdown.svelte +202 -0
  16. package/src/components/FloatingAction.svelte +367 -0
  17. package/src/components/FloatingNavigation.svelte +256 -0
  18. package/src/components/Grid.svelte +128 -0
  19. package/src/components/ItemContent.svelte +25 -0
  20. package/src/components/LazyTree.svelte +171 -0
  21. package/src/components/List.svelte +224 -0
  22. package/src/components/Menu.svelte +270 -0
  23. package/src/components/Message.svelte +75 -0
  24. package/src/components/MultiSelect.svelte +367 -0
  25. package/src/components/NavContent.svelte +42 -0
  26. package/src/components/PaletteManager.svelte +360 -0
  27. package/src/components/Pill.svelte +83 -0
  28. package/src/components/ProgressBar.svelte +31 -0
  29. package/src/components/Range.svelte +346 -0
  30. package/src/components/Rating.svelte +115 -0
  31. package/src/components/ResponsiveGrid.svelte +34 -0
  32. package/src/components/Reveal.svelte +46 -0
  33. package/src/components/SearchFilter.svelte +88 -0
  34. package/src/components/Select.svelte +424 -0
  35. package/src/components/Shine.svelte +87 -0
  36. package/src/components/Stack.svelte +38 -0
  37. package/src/components/StatusList.svelte +18 -0
  38. package/src/components/Stepper.svelte +174 -0
  39. package/src/components/Swatch.svelte +102 -0
  40. package/src/components/Switch.svelte +71 -0
  41. package/src/components/Table.svelte +245 -0
  42. package/src/components/Tabs.svelte +202 -0
  43. package/src/components/Tilt.svelte +68 -0
  44. package/src/components/Timeline.svelte +61 -0
  45. package/src/components/Toggle.svelte +93 -0
  46. package/src/components/Toolbar.svelte +306 -0
  47. package/src/components/ToolbarGroup.svelte +17 -0
  48. package/src/components/Tooltip.svelte +121 -0
  49. package/src/components/Tree.svelte +143 -0
  50. package/src/components/UploadFileStatus.svelte +102 -0
  51. package/src/components/UploadProgress.svelte +127 -0
  52. package/src/components/UploadTarget.svelte +126 -0
  53. package/src/components/index.ts +50 -0
  54. package/src/index.ts +61 -0
  55. package/src/markdown-plugin.ts +12 -0
  56. package/src/types/button.ts +86 -0
  57. package/src/types/code.ts +46 -0
  58. package/src/types/floating-action.ts +123 -0
  59. package/src/types/floating-navigation.ts +80 -0
  60. package/src/types/index.ts +58 -0
  61. package/src/types/list.ts +200 -0
  62. package/src/types/menu.ts +95 -0
  63. package/src/types/palette.ts +160 -0
  64. package/src/types/range.ts +51 -0
  65. package/src/types/search-filter.ts +67 -0
  66. package/src/types/select.ts +176 -0
  67. package/src/types/switch.ts +68 -0
  68. package/src/types/table.ts +219 -0
  69. package/src/types/tabs.ts +103 -0
  70. package/src/types/timeline.ts +53 -0
  71. package/src/types/toggle.ts +68 -0
  72. package/src/types/toolbar.ts +167 -0
  73. package/src/types/tooltip.ts +21 -0
  74. package/src/types/tree.ts +250 -0
  75. package/src/types/upload-file-status.ts +45 -0
  76. package/src/types/upload-progress.ts +111 -0
  77. package/src/types/upload-target.ts +71 -0
  78. package/src/utils/palette.ts +618 -0
  79. package/src/utils/shiki.ts +114 -0
  80. package/src/utils/upload.js +145 -0
  81. package/LICENSE +0 -21
  82. package/src/index.js +0 -8
  83. package/src/wrappers.js +0 -2
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Shiki syntax highlighting utility
3
+ */
4
+
5
+ import { createHighlighter, type Highlighter, type BundledLanguage, type BundledTheme } from 'shiki'
6
+
7
+ let highlighter: Highlighter | null = null
8
+ let isInitializing = false
9
+ let initPromise: Promise<Highlighter> | null = null
10
+
11
+ // Supported languages for the application
12
+ const SUPPORTED_LANGUAGES: BundledLanguage[] = [
13
+ 'svelte',
14
+ 'javascript',
15
+ 'typescript',
16
+ 'css',
17
+ 'html',
18
+ 'json',
19
+ 'bash',
20
+ 'shell',
21
+ 'markdown',
22
+ 'yaml',
23
+ 'sql'
24
+ ]
25
+
26
+ // Available themes
27
+ const THEMES: BundledTheme[] = ['github-light', 'github-dark']
28
+
29
+ /**
30
+ * Initialize Shiki highlighter (singleton)
31
+ */
32
+ function initializeHighlighter(): Promise<Highlighter> {
33
+ if (highlighter) return Promise.resolve(highlighter)
34
+
35
+ if (isInitializing && initPromise) {
36
+ return initPromise
37
+ }
38
+
39
+ isInitializing = true
40
+
41
+ initPromise = createHighlighter({
42
+ themes: THEMES,
43
+ langs: SUPPORTED_LANGUAGES
44
+ })
45
+ .then((hl: Highlighter) => {
46
+ highlighter = hl
47
+ isInitializing = false
48
+ return hl
49
+ })
50
+ .catch((error: Error) => {
51
+ isInitializing = false
52
+ initPromise = null
53
+ throw error
54
+ })
55
+
56
+ return initPromise
57
+ }
58
+
59
+ export interface HighlightOptions {
60
+ /** Language for syntax highlighting */
61
+ lang?: BundledLanguage | string
62
+ /** Theme to use (defaults based on user preference) */
63
+ theme?: BundledTheme | 'light' | 'dark'
64
+ }
65
+
66
+ /**
67
+ * Highlight code using Shiki
68
+ *
69
+ * @param code - The code to highlight
70
+ * @param options - Highlighting options
71
+ * @returns The highlighted code as HTML
72
+ */
73
+ function resolveTheme(themeOption: string | undefined): BundledTheme {
74
+ if (themeOption === 'light') return 'github-light'
75
+ if (themeOption === 'dark' || !themeOption) return 'github-dark'
76
+ return themeOption as BundledTheme
77
+ }
78
+
79
+ function isValidCode(code: unknown): code is string {
80
+ return Boolean(code) && typeof code === 'string'
81
+ }
82
+
83
+ export async function highlightCode(code: string, options: HighlightOptions = {}): Promise<string> {
84
+ if (!isValidCode(code)) throw new Error('Invalid code provided for highlighting')
85
+
86
+ const hl = await initializeHighlighter()
87
+ const lang = (options.lang ?? 'text') as BundledLanguage
88
+ const theme = resolveTheme(options.theme)
89
+ const loadedLangs = hl.getLoadedLanguages()
90
+ const effectiveLang = loadedLangs.includes(lang) ? lang : 'text'
91
+
92
+ return hl
93
+ .codeToHtml(code, { lang: effectiveLang, theme })
94
+ .replace(/(<pre[^>]+) style="[^"]*"/, '$1')
95
+ }
96
+
97
+ /**
98
+ * Preload the syntax highlighter for faster initial rendering
99
+ * Errors are silently ignored since preloading is optional
100
+ */
101
+ export async function preloadHighlighter(): Promise<void> {
102
+ try {
103
+ await initializeHighlighter()
104
+ } catch {
105
+ // Preload failures are non-critical - highlighting will still work on demand
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get list of supported languages
111
+ */
112
+ export function getSupportedLanguages(): readonly string[] {
113
+ return SUPPORTED_LANGUAGES
114
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Upload utility functions — shared helpers for UploadTarget and UploadProgress.
3
+ * Pure functions with no Svelte dependency.
4
+ */
5
+
6
+ const ARCHIVE_TYPES = new Set(['application/zip', 'application/gzip', 'application/x-tar'])
7
+
8
+ /**
9
+ * Check if a file matches an accept string (comma-separated MIME types and extensions).
10
+ * @param {{ type: string, name: string }} file
11
+ * @param {string | null | undefined} accept - e.g. 'image/*,.pdf,application/json'
12
+ * @returns {boolean}
13
+ */
14
+ export function matchesAccept(file, accept) {
15
+ if (!accept) return true
16
+
17
+ const tokens = accept
18
+ .split(',')
19
+ .map((t) => t.trim())
20
+ .filter(Boolean)
21
+ if (tokens.length === 0) return true
22
+
23
+ return tokens.some((token) => {
24
+ if (token.startsWith('.')) {
25
+ // Extension match (case-insensitive)
26
+ return file.name.toLowerCase().endsWith(token.toLowerCase())
27
+ }
28
+ if (token.endsWith('/*')) {
29
+ // Wildcard MIME type match (e.g. image/*)
30
+ const prefix = token.slice(0, -1) // 'image/'
31
+ return file.type.startsWith(prefix)
32
+ }
33
+ // Exact MIME type match
34
+ return file.type === token
35
+ })
36
+ }
37
+
38
+ /**
39
+ * Validate a file against accept and maxSize constraints.
40
+ * @param {{ type: string, name: string, size: number }} file
41
+ * @param {{ accept?: string, maxSize?: number }} constraints
42
+ * @returns {true | { reason: 'type' | 'size' }}
43
+ */
44
+ function hasTypeError(file, accept) {
45
+ return accept && !matchesAccept(file, accept)
46
+ }
47
+
48
+ function hasSizeError(file, maxSize) {
49
+ return maxSize !== undefined && file.size > maxSize
50
+ }
51
+
52
+ export function validateFile(file, { accept, maxSize } = {}) {
53
+ if (hasTypeError(file, accept)) return { reason: 'type' }
54
+ if (hasSizeError(file, maxSize)) return { reason: 'size' }
55
+ return true
56
+ }
57
+
58
+ /**
59
+ * Map a MIME type to an icon class string.
60
+ * @param {string | null | undefined} mimeType
61
+ * @returns {string}
62
+ */
63
+ const MIME_PREFIX_ICONS = [
64
+ ['image/', 'i-lucide:image'],
65
+ ['video/', 'i-lucide:video'],
66
+ ['audio/', 'i-lucide:music'],
67
+ ['text/', 'i-lucide:file-text'],
68
+ ['application/pdf', 'i-lucide:file-text']
69
+ ]
70
+
71
+ function getPrefixIcon(mimeType) {
72
+ for (const [prefix, icon] of MIME_PREFIX_ICONS) {
73
+ if (mimeType.startsWith(prefix)) return icon
74
+ }
75
+ return null
76
+ }
77
+
78
+ export function inferIcon(mimeType) {
79
+ if (!mimeType) return 'i-lucide:file'
80
+ const prefixIcon = getPrefixIcon(mimeType)
81
+ if (prefixIcon) return prefixIcon
82
+ if (ARCHIVE_TYPES.has(mimeType)) return 'i-lucide:archive'
83
+ return 'i-lucide:file'
84
+ }
85
+
86
+ /**
87
+ * Format a byte count as a human-readable size string.
88
+ * @param {number} bytes
89
+ * @returns {string}
90
+ */
91
+ export function formatSize(bytes) {
92
+ if (bytes < 1024) return `${bytes} B`
93
+ if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`
94
+ if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`
95
+ return `${(bytes / 1024 ** 3).toFixed(1)} GB`
96
+ }
97
+
98
+ /**
99
+ * Group a flat file array by path field into a nested tree structure.
100
+ * Root-level items (empty path) stay ungrouped.
101
+ * Group nodes use `text` as the label field (ProxyItem compatibility).
102
+ *
103
+ * @param {Array<Record<string, any>>} items
104
+ * @param {string} pathField - field name containing the path (e.g. 'path')
105
+ * @param {string} childrenField - field name for children arrays (e.g. 'children')
106
+ * @returns {Array<Record<string, any>>}
107
+ */
108
+ function resolveFolder(ctx, parent, segment, fullPath) {
109
+ let folder = ctx.folderMap.get(fullPath)
110
+ if (!folder) {
111
+ folder = { text: segment, [ctx.childrenField]: [] }
112
+ ctx.folderMap.set(fullPath, folder)
113
+ parent.push(folder)
114
+ }
115
+ return folder[ctx.childrenField]
116
+ }
117
+
118
+ function walkSegments(ctx, item, rawPath, root) {
119
+ const segments = rawPath.split('/').filter(Boolean)
120
+ let parent = root
121
+ let fullPath = ''
122
+ for (const segment of segments) {
123
+ fullPath = fullPath ? `${fullPath}/${segment}` : segment
124
+ parent = resolveFolder(ctx, parent, segment, fullPath)
125
+ }
126
+ parent.push(item)
127
+ }
128
+
129
+ export function groupByPath(items, pathField, childrenField) {
130
+ if (!items?.length) return []
131
+
132
+ const root = []
133
+ const ctx = { folderMap: new Map(), childrenField }
134
+
135
+ for (const item of items) {
136
+ const rawPath = item[pathField]
137
+ if (!rawPath) {
138
+ root.push(item)
139
+ continue
140
+ }
141
+ walkSegments(ctx, item, rawPath, root)
142
+ }
143
+
144
+ return root
145
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 Jerry Thomas
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/src/index.js DELETED
@@ -1,8 +0,0 @@
1
- // skipcq: JS-E1004 - Needed for exposing all components
2
- export * from '@rokkit/atoms'
3
- // skipcq: JS-E1004 - Needed for exposing all components
4
- export * from '@rokkit/molecules'
5
- // skipcq: JS-E1004 - Needed for exposing all components
6
- export * from '@rokkit/organisms'
7
- // skipcq: JS-E1004 - Needed for exposing all components
8
- export * from '@rokkit/layout'
package/src/wrappers.js DELETED
@@ -1,2 +0,0 @@
1
- // skipcq: JS-E1004 - Needed for exposing all components
2
- export * from '@rokkit/organisms/wrappers'