flarecms 0.1.0 → 0.1.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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/dist/auth/index.js +201 -1
  3. package/dist/cli/commands.js +5554 -55
  4. package/dist/cli/index.js +5554 -55
  5. package/dist/cli/mcp.js +30 -0
  6. package/dist/client/index.js +23576 -0
  7. package/dist/db/index.js +10392 -25
  8. package/dist/index.js +56776 -7582
  9. package/dist/server/index.js +43280 -0
  10. package/dist/style.css +5536 -0
  11. package/package.json +33 -30
  12. package/scripts/fix-api-paths.mjs +0 -32
  13. package/scripts/fix-imports.mjs +0 -38
  14. package/scripts/prefix-css.mjs +0 -45
  15. package/src/api/lib/cache.ts +0 -45
  16. package/src/api/lib/response.ts +0 -40
  17. package/src/api/middlewares/auth.ts +0 -186
  18. package/src/api/middlewares/cors.ts +0 -10
  19. package/src/api/middlewares/rbac.ts +0 -85
  20. package/src/api/routes/auth.ts +0 -377
  21. package/src/api/routes/collections.ts +0 -205
  22. package/src/api/routes/content.ts +0 -175
  23. package/src/api/routes/device.ts +0 -160
  24. package/src/api/routes/magic.ts +0 -150
  25. package/src/api/routes/mcp.ts +0 -273
  26. package/src/api/routes/oauth.ts +0 -160
  27. package/src/api/routes/settings.ts +0 -43
  28. package/src/api/routes/setup.ts +0 -307
  29. package/src/api/routes/tokens.ts +0 -80
  30. package/src/api/schemas/auth.ts +0 -15
  31. package/src/api/schemas/index.ts +0 -51
  32. package/src/api/schemas/tokens.ts +0 -24
  33. package/src/auth/index.ts +0 -28
  34. package/src/cli/commands.ts +0 -217
  35. package/src/cli/index.ts +0 -21
  36. package/src/cli/mcp.ts +0 -210
  37. package/src/cli/tests/cli.test.ts +0 -40
  38. package/src/cli/tests/create.test.ts +0 -87
  39. package/src/client/FlareAdminRouter.tsx +0 -47
  40. package/src/client/app.tsx +0 -175
  41. package/src/client/components/app-sidebar.tsx +0 -227
  42. package/src/client/components/collection-modal.tsx +0 -215
  43. package/src/client/components/content-list.tsx +0 -247
  44. package/src/client/components/dynamic-form.tsx +0 -190
  45. package/src/client/components/field-modal.tsx +0 -221
  46. package/src/client/components/settings/api-token-section.tsx +0 -400
  47. package/src/client/components/settings/general-section.tsx +0 -224
  48. package/src/client/components/settings/security-section.tsx +0 -154
  49. package/src/client/components/settings/seo-section.tsx +0 -200
  50. package/src/client/components/settings/signup-section.tsx +0 -257
  51. package/src/client/components/ui/accordion.tsx +0 -78
  52. package/src/client/components/ui/avatar.tsx +0 -107
  53. package/src/client/components/ui/badge.tsx +0 -52
  54. package/src/client/components/ui/button.tsx +0 -60
  55. package/src/client/components/ui/card.tsx +0 -103
  56. package/src/client/components/ui/checkbox.tsx +0 -27
  57. package/src/client/components/ui/collapsible.tsx +0 -19
  58. package/src/client/components/ui/dialog.tsx +0 -162
  59. package/src/client/components/ui/icon-picker.tsx +0 -485
  60. package/src/client/components/ui/icons-data.ts +0 -8476
  61. package/src/client/components/ui/input.tsx +0 -20
  62. package/src/client/components/ui/label.tsx +0 -20
  63. package/src/client/components/ui/popover.tsx +0 -91
  64. package/src/client/components/ui/select.tsx +0 -204
  65. package/src/client/components/ui/separator.tsx +0 -23
  66. package/src/client/components/ui/sheet.tsx +0 -141
  67. package/src/client/components/ui/sidebar.tsx +0 -722
  68. package/src/client/components/ui/skeleton.tsx +0 -13
  69. package/src/client/components/ui/sonner.tsx +0 -47
  70. package/src/client/components/ui/switch.tsx +0 -30
  71. package/src/client/components/ui/table.tsx +0 -116
  72. package/src/client/components/ui/tabs.tsx +0 -80
  73. package/src/client/components/ui/textarea.tsx +0 -18
  74. package/src/client/components/ui/tooltip.tsx +0 -68
  75. package/src/client/hooks/use-mobile.ts +0 -19
  76. package/src/client/index.css +0 -149
  77. package/src/client/index.ts +0 -7
  78. package/src/client/layouts/admin-layout.tsx +0 -93
  79. package/src/client/layouts/settings-layout.tsx +0 -104
  80. package/src/client/lib/api.ts +0 -72
  81. package/src/client/lib/utils.ts +0 -6
  82. package/src/client/main.tsx +0 -10
  83. package/src/client/pages/collection-detail.tsx +0 -634
  84. package/src/client/pages/collections.tsx +0 -180
  85. package/src/client/pages/dashboard.tsx +0 -133
  86. package/src/client/pages/device.tsx +0 -66
  87. package/src/client/pages/document-detail-page.tsx +0 -139
  88. package/src/client/pages/documents-page.tsx +0 -103
  89. package/src/client/pages/login.tsx +0 -345
  90. package/src/client/pages/settings.tsx +0 -65
  91. package/src/client/pages/setup.tsx +0 -129
  92. package/src/client/pages/signup.tsx +0 -188
  93. package/src/client/store/auth.ts +0 -30
  94. package/src/client/store/collections.ts +0 -13
  95. package/src/client/store/config.ts +0 -12
  96. package/src/client/store/fetcher.ts +0 -30
  97. package/src/client/store/router.ts +0 -95
  98. package/src/client/store/schema.ts +0 -39
  99. package/src/client/store/settings.ts +0 -31
  100. package/src/client/types.ts +0 -34
  101. package/src/db/dynamic.ts +0 -70
  102. package/src/db/index.ts +0 -16
  103. package/src/db/migrations/001_initial_schema.ts +0 -57
  104. package/src/db/migrations/002_auth_tables.ts +0 -84
  105. package/src/db/migrator.ts +0 -61
  106. package/src/db/schema.ts +0 -142
  107. package/src/index.ts +0 -12
  108. package/src/server/index.ts +0 -66
  109. package/src/types.ts +0 -20
  110. package/tests/css.test.ts +0 -21
  111. package/tests/modular.test.ts +0 -29
  112. package/tsconfig.json +0 -10
  113. /package/{style.css.d.ts → dist/style.css.d.ts} +0 -0
@@ -1,485 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import { useState, useMemo, useCallback, useEffect } from 'react';
5
- import {
6
- Popover,
7
- PopoverContent,
8
- PopoverTrigger,
9
- } from './popover';
10
- import { Button } from './button';
11
- import { Input } from './input';
12
- import { cn } from '../../lib/utils';
13
- import type { LucideProps, LucideIcon } from 'lucide-react';
14
- import {
15
- DynamicIcon,
16
- dynamicIconImports,
17
- type IconName,
18
- } from 'lucide-react/dynamic';
19
- import {
20
- Tooltip,
21
- TooltipContent,
22
- TooltipProvider,
23
- TooltipTrigger,
24
- } from './tooltip';
25
- import { iconsData } from './icons-data';
26
- import { useVirtualizer, type VirtualItem } from '@tanstack/react-virtual';
27
- import { Skeleton } from './skeleton';
28
- import Fuse from 'fuse.js';
29
- import { useDebounceValue } from 'usehooks-ts';
30
-
31
- export type IconData = (typeof iconsData)[number];
32
-
33
- interface IconPickerProps extends Omit<
34
- React.ComponentPropsWithoutRef<typeof PopoverTrigger>,
35
- 'onSelect' | 'onOpenChange'
36
- > {
37
- value?: IconName;
38
- defaultValue?: IconName;
39
- onValueChange?: (value: IconName) => void;
40
- open?: boolean;
41
- defaultOpen?: boolean;
42
- onOpenChange?: (open: boolean) => void;
43
- searchable?: boolean;
44
- searchPlaceholder?: string;
45
- triggerPlaceholder?: string;
46
- iconsList?: IconData[];
47
- categorized?: boolean;
48
- modal?: boolean;
49
- }
50
-
51
- const IconRenderer = React.memo(({ name }: { name: IconName }) => {
52
- return <Icon name={name} />;
53
- });
54
- IconRenderer.displayName = 'IconRenderer';
55
-
56
- const IconsColumnSkeleton = () => {
57
- return (
58
- <div className="flex flex-col gap-2 w-full">
59
- <Skeleton className="h-4 w-1/2 rounded-md" />
60
- <div className="grid grid-cols-5 gap-2 w-full">
61
- {Array.from({ length: 40 }).map((_, i) => (
62
- <Skeleton key={i} className="h-10 w-10 rounded-md" />
63
- ))}
64
- </div>
65
- </div>
66
- );
67
- };
68
-
69
- const useIconsData = () => {
70
- const [icons, setIcons] = useState<IconData[]>([]);
71
- const [isLoading, setIsLoading] = useState(true);
72
-
73
- useEffect(() => {
74
- let isMounted = true;
75
-
76
- const loadIcons = async () => {
77
- setIsLoading(true);
78
-
79
- const { iconsData } = await import('./icons-data');
80
- if (isMounted) {
81
- setIcons(
82
- iconsData.filter((icon: IconData) => {
83
- return icon.name in dynamicIconImports;
84
- }),
85
- );
86
- setIsLoading(false);
87
- }
88
- };
89
-
90
- loadIcons();
91
-
92
- return () => {
93
- isMounted = false;
94
- };
95
- }, []);
96
-
97
- return { icons, isLoading };
98
- };
99
-
100
- const IconPicker = React.forwardRef<
101
- React.ComponentRef<typeof PopoverTrigger>,
102
- IconPickerProps
103
- >(
104
- (
105
- {
106
- value,
107
- defaultValue,
108
- onValueChange,
109
- open,
110
- defaultOpen,
111
- onOpenChange,
112
- children,
113
- searchable = true,
114
- searchPlaceholder = 'Search for an icon...',
115
- triggerPlaceholder = 'Select an icon',
116
- iconsList,
117
- categorized = true,
118
- modal = false,
119
- ...props
120
- },
121
- ref,
122
- ) => {
123
- const [selectedIcon, setSelectedIcon] = useState<IconName | undefined>(
124
- defaultValue,
125
- );
126
- const [isOpen, setIsOpen] = useState(defaultOpen || false);
127
- const [search, setSearch] = useDebounceValue('', 100);
128
- const [isPopoverVisible, setIsPopoverVisible] = useState(false);
129
- const { icons } = useIconsData();
130
- const [isLoading, setIsLoading] = useState(true);
131
-
132
- const iconsToUse = useMemo(() => iconsList || icons, [iconsList, icons]);
133
-
134
- const fuseInstance = useMemo(() => {
135
- return new Fuse(iconsToUse, {
136
- keys: ['name', 'tags', 'categories'],
137
- threshold: 0.3,
138
- ignoreLocation: true,
139
- includeScore: true,
140
- });
141
- }, [iconsToUse]);
142
-
143
- const filteredIcons = useMemo(() => {
144
- if (search.trim() === '') {
145
- return iconsToUse;
146
- }
147
-
148
- const results = fuseInstance.search(search.toLowerCase().trim());
149
- return results.map((result) => result.item);
150
- }, [search, iconsToUse, fuseInstance]);
151
-
152
- const categorizedIcons = useMemo(() => {
153
- if (!categorized || search.trim() !== '') {
154
- return [{ name: 'All Icons', icons: filteredIcons }];
155
- }
156
-
157
- const categories = new Map<string, IconData[]>();
158
-
159
- filteredIcons.forEach((icon) => {
160
- if (icon.categories && icon.categories.length > 0) {
161
- icon.categories.forEach((category) => {
162
- if (!categories.has(category)) {
163
- categories.set(category, []);
164
- }
165
- categories.get(category)!.push(icon);
166
- });
167
- } else {
168
- const category = 'Other';
169
- if (!categories.has(category)) {
170
- categories.set(category, []);
171
- }
172
- categories.get(category)!.push(icon);
173
- }
174
- });
175
-
176
- return Array.from(categories.entries())
177
- .map(([name, icons]) => ({ name, icons }))
178
- .sort((a, b) => a.name.localeCompare(b.name));
179
- }, [filteredIcons, categorized, search]);
180
-
181
- const virtualItems = useMemo(() => {
182
- const items: Array<{
183
- type: 'category' | 'row';
184
- categoryIndex: number;
185
- rowIndex?: number;
186
- icons?: IconData[];
187
- }> = [];
188
-
189
- categorizedIcons.forEach((category, categoryIndex) => {
190
- items.push({ type: 'category', categoryIndex });
191
-
192
- const rows = [];
193
- for (let i = 0; i < category.icons.length; i += 5) {
194
- rows.push(category.icons.slice(i, i + 5));
195
- }
196
-
197
- rows.forEach((rowIcons, rowIndex) => {
198
- items.push({
199
- type: 'row',
200
- categoryIndex,
201
- rowIndex,
202
- icons: rowIcons,
203
- });
204
- });
205
- });
206
-
207
- return items;
208
- }, [categorizedIcons]);
209
-
210
- const categoryIndices = useMemo(() => {
211
- const indices: Record<string, number> = {};
212
-
213
- virtualItems.forEach((item, index) => {
214
- if (item.type === 'category') {
215
- indices[categorizedIcons[item.categoryIndex]!.name] = index;
216
- }
217
- });
218
-
219
- return indices;
220
- }, [virtualItems, categorizedIcons]);
221
-
222
- const parentRef = React.useRef<HTMLDivElement>(null);
223
-
224
- const virtualizer = useVirtualizer({
225
- count: virtualItems.length,
226
- getScrollElement: () => parentRef.current,
227
- estimateSize: (index) =>
228
- virtualItems[index]?.type === 'category' ? 25 : 40,
229
- paddingEnd: 2,
230
- gap: 10,
231
- overscan: 5,
232
- });
233
-
234
- const handleValueChange = useCallback(
235
- (icon: IconName) => {
236
- if (value === undefined) {
237
- setSelectedIcon(icon);
238
- }
239
- onValueChange?.(icon);
240
- },
241
- [value, onValueChange],
242
- );
243
-
244
- const handleOpenChange = useCallback(
245
- (newOpen: boolean) => {
246
- setSearch('');
247
- if (open === undefined) {
248
- setIsOpen(newOpen);
249
- }
250
- onOpenChange?.(newOpen);
251
-
252
- setIsPopoverVisible(newOpen);
253
-
254
- if (newOpen) {
255
- setTimeout(() => {
256
- virtualizer.measure();
257
- setIsLoading(false);
258
- }, 1);
259
- }
260
- },
261
- [open, onOpenChange, virtualizer],
262
- );
263
-
264
- const handleIconClick = useCallback(
265
- (iconName: IconName) => {
266
- handleValueChange(iconName);
267
- setIsOpen(false);
268
- setSearch('');
269
- },
270
- [handleValueChange],
271
- );
272
-
273
- const handleSearchChange = useCallback(
274
- (e: React.ChangeEvent<HTMLInputElement>) => {
275
- setSearch(e.target.value);
276
-
277
- if (parentRef.current) {
278
- parentRef.current.scrollTop = 0;
279
- }
280
-
281
- virtualizer.scrollToOffset(0);
282
- },
283
- [virtualizer],
284
- );
285
-
286
- const scrollToCategory = useCallback(
287
- (categoryName: string) => {
288
- const categoryIndex = categoryIndices[categoryName];
289
-
290
- if (categoryIndex !== undefined && virtualizer) {
291
- virtualizer.scrollToIndex(categoryIndex, {
292
- align: 'start',
293
- behavior: 'smooth',
294
- });
295
- }
296
- },
297
- [categoryIndices, virtualizer],
298
- );
299
-
300
- const categoryButtons = useMemo(() => {
301
- if (!categorized || search.trim() !== '') return null;
302
-
303
- return categorizedIcons.map((category) => (
304
- <Button
305
- key={category.name}
306
- variant={'outline'}
307
- size="sm"
308
- className="text-xs"
309
- onClick={(e) => {
310
- e.stopPropagation();
311
- scrollToCategory(category.name);
312
- }}
313
- >
314
- {category.name.charAt(0).toUpperCase() + category.name.slice(1)}
315
- </Button>
316
- ));
317
- }, [categorizedIcons, scrollToCategory, categorized, search]);
318
-
319
- const renderIcon = useCallback(
320
- (icon: IconData) => (
321
- <TooltipProvider key={icon.name}>
322
- <Tooltip>
323
- <TooltipTrigger
324
- className={cn(
325
- 'p-2 rounded-md border hover:bg-foreground/10 transition',
326
- 'flex items-center justify-center',
327
- )}
328
- onClick={() => handleIconClick(icon.name as IconName)}
329
- >
330
- <IconRenderer name={icon.name as IconName} />
331
- </TooltipTrigger>
332
- <TooltipContent>
333
- <p>{icon.name}</p>
334
- </TooltipContent>
335
- </Tooltip>
336
- </TooltipProvider>
337
- ),
338
- [handleIconClick],
339
- );
340
-
341
- const renderVirtualContent = useCallback(() => {
342
- if (filteredIcons.length === 0) {
343
- return <div className="text-center text-gray-500">No icon found</div>;
344
- }
345
-
346
- return (
347
- <div
348
- className="relative w-full overscroll-contain"
349
- style={{
350
- height: `${virtualizer.getTotalSize()}px`,
351
- }}
352
- >
353
- {virtualizer.getVirtualItems().map((virtualItem: VirtualItem) => {
354
- const item = virtualItems[virtualItem.index];
355
-
356
- if (!item) return null;
357
-
358
- const itemStyle = {
359
- position: 'absolute' as const,
360
- top: 0,
361
- left: 0,
362
- width: '100%',
363
- height: `${virtualItem.size}px`,
364
- transform: `translateY(${virtualItem.start}px)`,
365
- };
366
-
367
- if (item.type === 'category') {
368
- return (
369
- <div
370
- key={virtualItem.key}
371
- style={itemStyle}
372
- className="top-0 bg-background z-10"
373
- >
374
- <h3 className="font-medium text-sm capitalize">
375
- {categorizedIcons[item.categoryIndex]!.name}
376
- </h3>
377
- <div className="h-[1px] bg-foreground/10 w-full" />
378
- </div>
379
- );
380
- }
381
-
382
- return (
383
- <div
384
- key={virtualItem.key}
385
- data-index={virtualItem.index}
386
- style={itemStyle}
387
- >
388
- <div className="grid grid-cols-5 gap-2 w-full">
389
- {item.icons!.map(renderIcon)}
390
- </div>
391
- </div>
392
- );
393
- })}
394
- </div>
395
- );
396
- }, [
397
- virtualizer,
398
- virtualItems,
399
- categorizedIcons,
400
- filteredIcons,
401
- renderIcon,
402
- ]);
403
-
404
- React.useEffect(() => {
405
- if (isPopoverVisible) {
406
- setIsLoading(true);
407
- const timer = setTimeout(() => {
408
- setIsLoading(false);
409
- virtualizer.measure();
410
- }, 10);
411
-
412
- const resizeObserver = new ResizeObserver(() => {
413
- virtualizer.measure();
414
- });
415
-
416
- if (parentRef.current) {
417
- resizeObserver.observe(parentRef.current);
418
- }
419
-
420
- return () => {
421
- clearTimeout(timer);
422
- resizeObserver.disconnect();
423
- };
424
- }
425
- }, [isPopoverVisible, virtualizer]);
426
-
427
- return (
428
- <Popover
429
- open={open ?? isOpen}
430
- onOpenChange={handleOpenChange}
431
- modal={modal}
432
- >
433
- <PopoverTrigger asChild ref={ref} {...props}>
434
- {children || (
435
- <Button variant="outline">
436
- {value || selectedIcon ? (
437
- <>
438
- <Icon name={(value || selectedIcon)!} />{' '}
439
- {value || selectedIcon}
440
- </>
441
- ) : (
442
- triggerPlaceholder
443
- )}
444
- </Button>
445
- )}
446
- </PopoverTrigger>
447
- <PopoverContent className="w-64 p-2">
448
- {searchable && (
449
- <Input
450
- placeholder={searchPlaceholder}
451
- onChange={handleSearchChange}
452
- className="mb-2"
453
- />
454
- )}
455
- {categorized && search.trim() === '' && (
456
- <div className="flex flex-row gap-1 mt-2 overflow-x-auto pb-2">
457
- {categoryButtons}
458
- </div>
459
- )}
460
- <div
461
- ref={parentRef}
462
- className="max-h-60 overflow-auto"
463
- style={{ scrollbarWidth: 'thin' }}
464
- >
465
- {isLoading ? <IconsColumnSkeleton /> : renderVirtualContent()}
466
- </div>
467
- </PopoverContent>
468
- </Popover>
469
- );
470
- },
471
- );
472
- IconPicker.displayName = 'IconPicker';
473
-
474
- interface IconProps extends Omit<LucideProps, 'ref'> {
475
- name: IconName;
476
- }
477
-
478
- const Icon = React.forwardRef<React.ComponentRef<LucideIcon>, IconProps>(
479
- ({ name, ...props }, ref) => {
480
- return <DynamicIcon name={name} {...props} ref={ref} />;
481
- },
482
- );
483
- Icon.displayName = 'Icon';
484
-
485
- export { IconPicker, Icon, type IconName };