@khal-os/ui 1.0.0 → 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.
- package/LICENSE +94 -0
- package/README.md +25 -0
- package/dist/index.cjs +2661 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +926 -0
- package/dist/index.d.ts +926 -0
- package/dist/index.js +2510 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -40
- package/tokens.css +260 -238
- package/src/components/ContextMenu.tsx +0 -130
- package/src/components/avatar.tsx +0 -71
- package/src/components/badge.tsx +0 -39
- package/src/components/button.tsx +0 -102
- package/src/components/command.tsx +0 -165
- package/src/components/cost-counter.tsx +0 -75
- package/src/components/data-row.tsx +0 -97
- package/src/components/dropdown-menu.tsx +0 -233
- package/src/components/glass-card.tsx +0 -74
- package/src/components/input.tsx +0 -48
- package/src/components/khal-logo.tsx +0 -73
- package/src/components/live-feed.tsx +0 -109
- package/src/components/mesh-gradient.tsx +0 -57
- package/src/components/metric-display.tsx +0 -93
- package/src/components/note.tsx +0 -55
- package/src/components/number-flow.tsx +0 -25
- package/src/components/pill-badge.tsx +0 -65
- package/src/components/progress-bar.tsx +0 -70
- package/src/components/section-card.tsx +0 -76
- package/src/components/separator.tsx +0 -25
- package/src/components/spinner.tsx +0 -42
- package/src/components/status-dot.tsx +0 -90
- package/src/components/switch.tsx +0 -36
- package/src/components/theme-provider.tsx +0 -58
- package/src/components/theme-switcher.tsx +0 -59
- package/src/components/ticker-bar.tsx +0 -41
- package/src/components/tooltip.tsx +0 -62
- package/src/components/window-minimized-context.tsx +0 -29
- package/src/hooks/useReducedMotion.ts +0 -21
- package/src/index.ts +0 -58
- package/src/lib/animations.ts +0 -50
- package/src/primitives/collapsible-sidebar.tsx +0 -226
- package/src/primitives/dialog.tsx +0 -76
- package/src/primitives/empty-state.tsx +0 -43
- package/src/primitives/index.ts +0 -22
- package/src/primitives/list-view.tsx +0 -155
- package/src/primitives/property-panel.tsx +0 -108
- package/src/primitives/section-header.tsx +0 -19
- package/src/primitives/sidebar-nav.tsx +0 -110
- package/src/primitives/split-pane.tsx +0 -146
- package/src/primitives/status-badge.tsx +0 -10
- package/src/primitives/status-bar.tsx +0 -100
- package/src/primitives/toolbar.tsx +0 -152
- package/src/server.ts +0 -4
- package/src/stores/notification-store.ts +0 -271
- package/src/stores/theme-store.ts +0 -33
- package/src/tokens/lp-tokens.ts +0 -36
- package/src/utils.ts +0 -6
- package/tsconfig.json +0 -17
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
4
|
-
import * as React from 'react';
|
|
5
|
-
import { cn } from '../utils';
|
|
6
|
-
|
|
7
|
-
const TooltipProvider = TooltipPrimitive.Provider;
|
|
8
|
-
const TooltipRoot = TooltipPrimitive.Root;
|
|
9
|
-
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
10
|
-
|
|
11
|
-
const TooltipContent = React.forwardRef<
|
|
12
|
-
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
|
13
|
-
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
14
|
-
>(({ className, sideOffset = 4, style, ...props }, ref) => (
|
|
15
|
-
<TooltipPrimitive.Portal>
|
|
16
|
-
<TooltipPrimitive.Content
|
|
17
|
-
ref={ref}
|
|
18
|
-
sideOffset={sideOffset}
|
|
19
|
-
className={cn(
|
|
20
|
-
'z-[9999] overflow-hidden rounded-md px-2.5 py-1 text-label-12',
|
|
21
|
-
'animate-in fade-in-0 zoom-in-95',
|
|
22
|
-
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
23
|
-
'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
24
|
-
className
|
|
25
|
-
)}
|
|
26
|
-
style={{
|
|
27
|
-
background: 'var(--khal-text-primary, var(--ds-gray-1000))',
|
|
28
|
-
color: 'var(--khal-text-inverse, var(--ds-background-100))',
|
|
29
|
-
...style,
|
|
30
|
-
}}
|
|
31
|
-
{...props}
|
|
32
|
-
/>
|
|
33
|
-
</TooltipPrimitive.Portal>
|
|
34
|
-
));
|
|
35
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
36
|
-
|
|
37
|
-
interface SimpleTooltipProps {
|
|
38
|
-
text: React.ReactNode;
|
|
39
|
-
children: React.ReactNode;
|
|
40
|
-
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
41
|
-
delay?: boolean;
|
|
42
|
-
delayTime?: number;
|
|
43
|
-
desktopOnly?: boolean;
|
|
44
|
-
className?: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function Tooltip({ text, children, position = 'top', delay, delayTime, desktopOnly, className }: SimpleTooltipProps) {
|
|
48
|
-
const delayDuration = delayTime ?? (delay ? 400 : 200);
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<TooltipProvider delayDuration={delayDuration}>
|
|
52
|
-
<TooltipRoot>
|
|
53
|
-
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
54
|
-
<TooltipContent side={position} className={cn(desktopOnly && 'max-md:hidden', className)}>
|
|
55
|
-
{text}
|
|
56
|
-
</TooltipContent>
|
|
57
|
-
</TooltipRoot>
|
|
58
|
-
</TooltipProvider>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export { Tooltip, TooltipContent, TooltipProvider, TooltipRoot, TooltipTrigger };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext } from 'react';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Exposes the parent window's minimized state to deeply nested child components.
|
|
7
|
-
* Terminal panes use this to dispose WebGL contexts when minimized (GPU savings)
|
|
8
|
-
* and re-attach them on restore.
|
|
9
|
-
*/
|
|
10
|
-
const WindowMinimizedContext = createContext(false);
|
|
11
|
-
|
|
12
|
-
export const WindowMinimizedProvider = WindowMinimizedContext.Provider;
|
|
13
|
-
|
|
14
|
-
export function useWindowMinimized(): boolean {
|
|
15
|
-
return useContext(WindowMinimizedContext);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Exposes the parent window's focused/active state to child components.
|
|
20
|
-
* Apps use this to pause polling, animations, and heavy rendering when
|
|
21
|
-
* their window is behind another (app-nap behavior).
|
|
22
|
-
*/
|
|
23
|
-
const WindowActiveContext = createContext(true);
|
|
24
|
-
|
|
25
|
-
export const WindowActiveProvider = WindowActiveContext.Provider;
|
|
26
|
-
|
|
27
|
-
export function useWindowActive(): boolean {
|
|
28
|
-
return useContext(WindowActiveContext);
|
|
29
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns true when the user prefers reduced motion (OS setting or app toggle).
|
|
7
|
-
*/
|
|
8
|
-
export function useReducedMotion(): boolean {
|
|
9
|
-
const [reduced, setReduced] = useState(false);
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
13
|
-
setReduced(mq.matches || document.documentElement.dataset.reduceMotion === 'true');
|
|
14
|
-
|
|
15
|
-
const handler = (e: MediaQueryListEvent) => setReduced(e.matches);
|
|
16
|
-
mq.addEventListener('change', handler);
|
|
17
|
-
return () => mq.removeEventListener('change', handler);
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
return reduced;
|
|
21
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// App component props type
|
|
2
|
-
export interface AppComponentProps {
|
|
3
|
-
windowId: string;
|
|
4
|
-
meta?: Record<string, unknown>;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// Auth — re-export from SDK
|
|
8
|
-
export { SUBJECTS, useKhalAuth, useKhalAuth as useOSAuth, useNats, useNatsSubscription } from '@khal-os/sdk/app';
|
|
9
|
-
// shadcn/ui components — local implementations
|
|
10
|
-
export * from './components/avatar';
|
|
11
|
-
export * from './components/badge';
|
|
12
|
-
export * from './components/button';
|
|
13
|
-
export * from './components/ContextMenu';
|
|
14
|
-
export * from './components/command';
|
|
15
|
-
// Design system components — local implementations
|
|
16
|
-
export * from './components/cost-counter';
|
|
17
|
-
// Design system components — LP section patterns
|
|
18
|
-
export * from './components/data-row';
|
|
19
|
-
export * from './components/dropdown-menu';
|
|
20
|
-
export * from './components/glass-card';
|
|
21
|
-
export * from './components/input';
|
|
22
|
-
export * from './components/khal-logo';
|
|
23
|
-
export * from './components/live-feed';
|
|
24
|
-
export * from './components/mesh-gradient';
|
|
25
|
-
export * from './components/metric-display';
|
|
26
|
-
export * from './components/note';
|
|
27
|
-
export * from './components/number-flow';
|
|
28
|
-
export * from './components/pill-badge';
|
|
29
|
-
export * from './components/progress-bar';
|
|
30
|
-
export * from './components/section-card';
|
|
31
|
-
export * from './components/separator';
|
|
32
|
-
export * from './components/spinner';
|
|
33
|
-
export * from './components/status-dot';
|
|
34
|
-
export * from './components/switch';
|
|
35
|
-
export * from './components/theme-provider';
|
|
36
|
-
export * from './components/theme-switcher';
|
|
37
|
-
export * from './components/ticker-bar';
|
|
38
|
-
export * from './components/tooltip';
|
|
39
|
-
export {
|
|
40
|
-
useWindowActive,
|
|
41
|
-
useWindowMinimized,
|
|
42
|
-
WindowActiveProvider,
|
|
43
|
-
WindowMinimizedProvider,
|
|
44
|
-
} from './components/window-minimized-context';
|
|
45
|
-
// Hooks
|
|
46
|
-
export { useReducedMotion } from './hooks/useReducedMotion';
|
|
47
|
-
// Animations
|
|
48
|
-
export { fadeIn, fadeUp, khalEasing, scaleUp, springConfig, staggerChild, staggerContainer } from './lib/animations';
|
|
49
|
-
// OS Primitives — local implementations
|
|
50
|
-
export * from './primitives';
|
|
51
|
-
export type { DesktopNotification, DesktopNotifMode, NotificationUrgency, TrayIcon } from './stores/notification-store';
|
|
52
|
-
// Stores (OS-level state)
|
|
53
|
-
export { useNotificationStore } from './stores/notification-store';
|
|
54
|
-
export { useThemeStore } from './stores/theme-store';
|
|
55
|
-
// LP Design Tokens
|
|
56
|
-
export * from './tokens/lp-tokens';
|
|
57
|
-
// Utilities
|
|
58
|
-
export { cn } from './utils';
|
package/src/lib/animations.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KhalOS animation presets — motion.js configurations for OS-wide use.
|
|
3
|
-
* Import these in components for consistent, branded animations.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/** Custom easing curve — KhalOS primary */
|
|
7
|
-
export const khalEasing = [0.22, 1, 0.36, 1] as const;
|
|
8
|
-
|
|
9
|
-
/** Spring config for interactive elements */
|
|
10
|
-
export const springConfig = {
|
|
11
|
-
stiffness: 300,
|
|
12
|
-
damping: 22,
|
|
13
|
-
} as const;
|
|
14
|
-
|
|
15
|
-
/** Window open animation — fade up with blur */
|
|
16
|
-
export const fadeUp = {
|
|
17
|
-
initial: { opacity: 0, y: 12, filter: 'blur(4px)' },
|
|
18
|
-
animate: { opacity: 1, y: 0, filter: 'blur(0px)' },
|
|
19
|
-
transition: { duration: 0.7, ease: khalEasing },
|
|
20
|
-
} as const;
|
|
21
|
-
|
|
22
|
-
/** App launch animation — scale up with blur */
|
|
23
|
-
export const scaleUp = {
|
|
24
|
-
initial: { opacity: 0, scale: 0.96, filter: 'blur(6px)' },
|
|
25
|
-
animate: { opacity: 1, scale: 1, filter: 'blur(0px)' },
|
|
26
|
-
transition: { duration: 0.9, ease: khalEasing },
|
|
27
|
-
} as const;
|
|
28
|
-
|
|
29
|
-
/** Stagger container — children appear with 0.12s delay */
|
|
30
|
-
export const staggerContainer = {
|
|
31
|
-
animate: {
|
|
32
|
-
transition: {
|
|
33
|
-
staggerChildren: 0.12,
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
} as const;
|
|
37
|
-
|
|
38
|
-
/** Stagger child — each item fades up */
|
|
39
|
-
export const staggerChild = {
|
|
40
|
-
initial: { opacity: 0, y: 8 },
|
|
41
|
-
animate: { opacity: 1, y: 0 },
|
|
42
|
-
transition: { duration: 0.4, ease: khalEasing },
|
|
43
|
-
} as const;
|
|
44
|
-
|
|
45
|
-
/** Fade in — simple opacity animation */
|
|
46
|
-
export const fadeIn = {
|
|
47
|
-
initial: { opacity: 0 },
|
|
48
|
-
animate: { opacity: 1 },
|
|
49
|
-
transition: { duration: 0.5, ease: khalEasing },
|
|
50
|
-
} as const;
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { createContext, type ReactNode, useCallback, useContext, useRef, useState } from 'react';
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// CollapsibleSidebar — resizable sidebar with collapse/expand toggle.
|
|
7
|
-
//
|
|
8
|
-
// Usage:
|
|
9
|
-
// <CollapsibleSidebar defaultSize={220} min={160} max={360}>
|
|
10
|
-
// <CollapsibleSidebar.Header>
|
|
11
|
-
// <span>Explorer</span>
|
|
12
|
-
// <CollapsibleSidebar.CollapseButton />
|
|
13
|
-
// </CollapsibleSidebar.Header>
|
|
14
|
-
// <CollapsibleSidebar.Content>
|
|
15
|
-
// <FileTree />
|
|
16
|
-
// </CollapsibleSidebar.Content>
|
|
17
|
-
// </CollapsibleSidebar>
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
|
|
20
|
-
interface SidebarContextValue {
|
|
21
|
-
collapsed: boolean;
|
|
22
|
-
toggle: () => void;
|
|
23
|
-
size: number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const SidebarContext = createContext<SidebarContextValue>({
|
|
27
|
-
collapsed: false,
|
|
28
|
-
toggle: () => {},
|
|
29
|
-
size: 220,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
export function useSidebar() {
|
|
33
|
-
return useContext(SidebarContext);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface CollapsibleSidebarProps {
|
|
37
|
-
children: ReactNode;
|
|
38
|
-
defaultSize?: number;
|
|
39
|
-
min?: number;
|
|
40
|
-
max?: number;
|
|
41
|
-
defaultCollapsed?: boolean;
|
|
42
|
-
side?: 'left' | 'right';
|
|
43
|
-
className?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function CollapsibleSidebarRoot({
|
|
47
|
-
children,
|
|
48
|
-
defaultSize = 220,
|
|
49
|
-
min = 140,
|
|
50
|
-
max = 400,
|
|
51
|
-
defaultCollapsed = false,
|
|
52
|
-
side = 'left',
|
|
53
|
-
className = '',
|
|
54
|
-
}: CollapsibleSidebarProps) {
|
|
55
|
-
const [collapsed, setCollapsed] = useState(defaultCollapsed);
|
|
56
|
-
const [size, setSize] = useState(defaultSize);
|
|
57
|
-
const dragging = useRef(false);
|
|
58
|
-
const startX = useRef(0);
|
|
59
|
-
const startSize = useRef(0);
|
|
60
|
-
|
|
61
|
-
const toggle = useCallback(() => setCollapsed((v) => !v), []);
|
|
62
|
-
|
|
63
|
-
const onPointerDown = useCallback(
|
|
64
|
-
(e: React.PointerEvent) => {
|
|
65
|
-
if (collapsed) return;
|
|
66
|
-
e.preventDefault();
|
|
67
|
-
dragging.current = true;
|
|
68
|
-
startX.current = e.clientX;
|
|
69
|
-
startSize.current = size;
|
|
70
|
-
(e.target as HTMLElement).setPointerCapture(e.pointerId);
|
|
71
|
-
},
|
|
72
|
-
[collapsed, size]
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const onPointerMove = useCallback(
|
|
76
|
-
(e: React.PointerEvent) => {
|
|
77
|
-
if (!dragging.current) return;
|
|
78
|
-
const delta = side === 'left' ? e.clientX - startX.current : startX.current - e.clientX;
|
|
79
|
-
setSize(Math.min(max, Math.max(min, startSize.current + delta)));
|
|
80
|
-
},
|
|
81
|
-
[side, min, max]
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const onPointerUp = useCallback(() => {
|
|
85
|
-
dragging.current = false;
|
|
86
|
-
}, []);
|
|
87
|
-
|
|
88
|
-
const resizeHandle = (
|
|
89
|
-
<div
|
|
90
|
-
className={`w-px shrink-0 cursor-col-resize bg-gray-alpha-200 transition-colors hover:w-0.5 hover:bg-blue-700/50 active:w-0.5 active:bg-blue-700 ${
|
|
91
|
-
collapsed ? 'pointer-events-none' : ''
|
|
92
|
-
}`}
|
|
93
|
-
onPointerDown={onPointerDown}
|
|
94
|
-
onPointerMove={onPointerMove}
|
|
95
|
-
onPointerUp={onPointerUp}
|
|
96
|
-
role="separator"
|
|
97
|
-
aria-orientation="vertical"
|
|
98
|
-
/>
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<SidebarContext.Provider value={{ collapsed, toggle, size }}>
|
|
103
|
-
<div className={`flex shrink-0 ${className}`} style={{ width: collapsed ? 0 : size }}>
|
|
104
|
-
{side === 'right' && resizeHandle}
|
|
105
|
-
<div
|
|
106
|
-
className={`flex h-full flex-col overflow-hidden transition-[width] duration-150 ${
|
|
107
|
-
collapsed ? 'w-0' : 'w-full'
|
|
108
|
-
}`}
|
|
109
|
-
>
|
|
110
|
-
{children}
|
|
111
|
-
</div>
|
|
112
|
-
{side === 'left' && resizeHandle}
|
|
113
|
-
</div>
|
|
114
|
-
</SidebarContext.Provider>
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
// CollapsibleSidebar.Header
|
|
120
|
-
// ---------------------------------------------------------------------------
|
|
121
|
-
|
|
122
|
-
function SidebarHeader({ children, className = '' }: { children: ReactNode; className?: string }) {
|
|
123
|
-
return (
|
|
124
|
-
<div className={`flex h-9 shrink-0 items-center justify-between border-b border-gray-alpha-200 px-3 ${className}`}>
|
|
125
|
-
{children}
|
|
126
|
-
</div>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// ---------------------------------------------------------------------------
|
|
131
|
-
// CollapsibleSidebar.CollapseButton
|
|
132
|
-
// ---------------------------------------------------------------------------
|
|
133
|
-
|
|
134
|
-
function CollapseButton({ className = '' }: { className?: string }) {
|
|
135
|
-
const { collapsed, toggle } = useSidebar();
|
|
136
|
-
return (
|
|
137
|
-
<button
|
|
138
|
-
onClick={toggle}
|
|
139
|
-
className={`inline-flex h-5 w-5 items-center justify-center rounded text-gray-800 hover:bg-gray-alpha-200 hover:text-gray-1000 transition-colors ${className}`}
|
|
140
|
-
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
141
|
-
>
|
|
142
|
-
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
|
143
|
-
<path
|
|
144
|
-
d={collapsed ? 'M4.5 2L8.5 6L4.5 10' : 'M8.5 2L4.5 6L8.5 10'}
|
|
145
|
-
stroke="currentColor"
|
|
146
|
-
strokeWidth="1.5"
|
|
147
|
-
strokeLinecap="round"
|
|
148
|
-
strokeLinejoin="round"
|
|
149
|
-
/>
|
|
150
|
-
</svg>
|
|
151
|
-
</button>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// ---------------------------------------------------------------------------
|
|
156
|
-
// CollapsibleSidebar.Content
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
|
|
159
|
-
function SidebarContent({ children, className = '' }: { children: ReactNode; className?: string }) {
|
|
160
|
-
return <div className={`flex-1 overflow-y-auto ${className}`}>{children}</div>;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ---------------------------------------------------------------------------
|
|
164
|
-
// CollapsibleSidebar.Section
|
|
165
|
-
// ---------------------------------------------------------------------------
|
|
166
|
-
|
|
167
|
-
function SidebarSection({
|
|
168
|
-
title,
|
|
169
|
-
children,
|
|
170
|
-
className = '',
|
|
171
|
-
}: {
|
|
172
|
-
title?: string;
|
|
173
|
-
children: ReactNode;
|
|
174
|
-
className?: string;
|
|
175
|
-
}) {
|
|
176
|
-
return (
|
|
177
|
-
<div className={`${className}`}>
|
|
178
|
-
{title && (
|
|
179
|
-
<div className="px-3 pt-2 pb-1">
|
|
180
|
-
<span className="text-label-13 font-medium text-gray-800">{title}</span>
|
|
181
|
-
</div>
|
|
182
|
-
)}
|
|
183
|
-
{children}
|
|
184
|
-
</div>
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// ---------------------------------------------------------------------------
|
|
189
|
-
// CollapsibleSidebar.Item
|
|
190
|
-
// ---------------------------------------------------------------------------
|
|
191
|
-
|
|
192
|
-
interface SidebarItemProps {
|
|
193
|
-
children: ReactNode;
|
|
194
|
-
icon?: ReactNode;
|
|
195
|
-
active?: boolean;
|
|
196
|
-
indent?: number;
|
|
197
|
-
onClick?: () => void;
|
|
198
|
-
className?: string;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function SidebarItem({ children, icon, active, indent = 0, onClick, className = '' }: SidebarItemProps) {
|
|
202
|
-
return (
|
|
203
|
-
<button
|
|
204
|
-
onClick={onClick}
|
|
205
|
-
className={`flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-label-13 transition-colors
|
|
206
|
-
${active ? 'bg-gray-alpha-200 text-gray-1000' : 'text-gray-900 hover:bg-gray-alpha-100 hover:text-gray-1000'}
|
|
207
|
-
${className}`}
|
|
208
|
-
style={{ paddingLeft: 8 + indent * 12 }}
|
|
209
|
-
>
|
|
210
|
-
{icon && <span className="shrink-0 text-gray-800 [&>svg]:h-3.5 [&>svg]:w-3.5">{icon}</span>}
|
|
211
|
-
<span className="min-w-0 truncate">{children}</span>
|
|
212
|
-
</button>
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// ---------------------------------------------------------------------------
|
|
217
|
-
// Export
|
|
218
|
-
// ---------------------------------------------------------------------------
|
|
219
|
-
|
|
220
|
-
export const CollapsibleSidebar = Object.assign(CollapsibleSidebarRoot, {
|
|
221
|
-
Header: SidebarHeader,
|
|
222
|
-
CollapseButton,
|
|
223
|
-
Content: SidebarContent,
|
|
224
|
-
Section: SidebarSection,
|
|
225
|
-
Item: SidebarItem,
|
|
226
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
|
4
|
-
|
|
5
|
-
/* ------------------------------------------------------------------ */
|
|
6
|
-
/* Minimal compound Dialog primitive for OS chrome. */
|
|
7
|
-
/* Enough to satisfy DeleteConfirmDialog; expand as needed. */
|
|
8
|
-
/* ------------------------------------------------------------------ */
|
|
9
|
-
|
|
10
|
-
interface DialogRootProps {
|
|
11
|
-
open: boolean;
|
|
12
|
-
onClose: () => void;
|
|
13
|
-
children: ReactNode;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function DialogRoot({ open, onClose, children }: DialogRootProps) {
|
|
17
|
-
if (!open) return null;
|
|
18
|
-
return (
|
|
19
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40" onClick={onClose}>
|
|
20
|
-
<div
|
|
21
|
-
className="bg-popover text-popover-foreground rounded-lg border p-6 shadow-lg"
|
|
22
|
-
onClick={(e) => e.stopPropagation()}
|
|
23
|
-
>
|
|
24
|
-
{children}
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function Body({ children }: { children: ReactNode }) {
|
|
31
|
-
return <div className="flex items-start gap-4">{children}</div>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function Icon({ children, variant: _variant }: { children: ReactNode; variant?: string }) {
|
|
35
|
-
return <div className="shrink-0">{children}</div>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function Title({ children }: { children: ReactNode }) {
|
|
39
|
-
return <h2 className="text-lg font-semibold">{children}</h2>;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function Description({ children }: { children: ReactNode }) {
|
|
43
|
-
return <p className="text-muted-foreground text-sm">{children}</p>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function Actions({ children }: { children: ReactNode }) {
|
|
47
|
-
return <div className="mt-4 flex justify-end gap-2">{children}</div>;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
type BtnProps = ButtonHTMLAttributes<HTMLButtonElement> & { variant?: string };
|
|
51
|
-
|
|
52
|
-
function Cancel({ children, ...props }: BtnProps) {
|
|
53
|
-
return (
|
|
54
|
-
<button type="button" className="rounded px-3 py-1.5 text-sm hover:bg-muted" {...props}>
|
|
55
|
-
{children}
|
|
56
|
-
</button>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function Confirm({ children, variant: _variant, ...props }: BtnProps) {
|
|
61
|
-
return (
|
|
62
|
-
<button type="button" className="rounded bg-destructive px-3 py-1.5 text-sm text-destructive-foreground" {...props}>
|
|
63
|
-
{children}
|
|
64
|
-
</button>
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export const Dialog = Object.assign(DialogRoot, {
|
|
69
|
-
Body,
|
|
70
|
-
Icon,
|
|
71
|
-
Title,
|
|
72
|
-
Description,
|
|
73
|
-
Actions,
|
|
74
|
-
Cancel,
|
|
75
|
-
Confirm,
|
|
76
|
-
});
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { type ReactNode } from 'react';
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// EmptyState — placeholder for empty views (no files, no results, etc.).
|
|
7
|
-
//
|
|
8
|
-
// Usage:
|
|
9
|
-
// <EmptyState
|
|
10
|
-
// icon={<FolderOpen />}
|
|
11
|
-
// title="No files"
|
|
12
|
-
// description="This folder is empty."
|
|
13
|
-
// action={<Button onClick={upload}>Upload File</Button>}
|
|
14
|
-
// />
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
interface EmptyStateProps {
|
|
18
|
-
icon?: ReactNode;
|
|
19
|
-
title: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
action?: ReactNode;
|
|
22
|
-
compact?: boolean;
|
|
23
|
-
className?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function EmptyState({ icon, title, description, action, compact, className = '' }: EmptyStateProps) {
|
|
27
|
-
return (
|
|
28
|
-
<div
|
|
29
|
-
className={`flex flex-col items-center justify-center text-center ${
|
|
30
|
-
compact ? 'gap-2 py-6' : 'gap-3 py-12'
|
|
31
|
-
} ${className}`}
|
|
32
|
-
>
|
|
33
|
-
{icon && (
|
|
34
|
-
<div className={`text-gray-700 ${compact ? '[&>svg]:h-5 [&>svg]:w-5' : '[&>svg]:h-8 [&>svg]:w-8'}`}>{icon}</div>
|
|
35
|
-
)}
|
|
36
|
-
<div className="space-y-0.5">
|
|
37
|
-
<p className="text-label-13 font-medium text-gray-1000">{title}</p>
|
|
38
|
-
{description && <p className={`text-gray-800 ${compact ? 'text-label-13' : 'text-label-13'}`}>{description}</p>}
|
|
39
|
-
</div>
|
|
40
|
-
{action && <div className="mt-2 shrink-0">{action}</div>}
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
}
|
package/src/primitives/index.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
// OS Primitives — UI building blocks for desktop app chrome.
|
|
2
|
-
// Built on top of shadcn/ui components and Geist design tokens.
|
|
3
|
-
//
|
|
4
|
-
// These fill the gap between shadcn's web-focused components and the
|
|
5
|
-
// needs of a desktop-style app (toolbars, split panes, status bars, etc.).
|
|
6
|
-
//
|
|
7
|
-
// shadcn/ui components to use directly (from @/components/ui/*):
|
|
8
|
-
// Button, Input, Badge, Spinner, Separator, Tooltip, Toggle (Switch),
|
|
9
|
-
// ContextMenu, Command (CommandDialog), DropdownMenu, Note, LoadingDots,
|
|
10
|
-
// ThemeSwitcher
|
|
11
|
-
|
|
12
|
-
export { CollapsibleSidebar, useSidebar } from './collapsible-sidebar';
|
|
13
|
-
export { Dialog } from './dialog';
|
|
14
|
-
export { EmptyState } from './empty-state';
|
|
15
|
-
export { ListView } from './list-view';
|
|
16
|
-
export { PropertyPanel } from './property-panel';
|
|
17
|
-
export { SectionHeader } from './section-header';
|
|
18
|
-
export { SidebarNav } from './sidebar-nav';
|
|
19
|
-
export { SplitPane } from './split-pane';
|
|
20
|
-
export { StatusBadge } from './status-badge';
|
|
21
|
-
export { StatusBar } from './status-bar';
|
|
22
|
-
export { Toolbar } from './toolbar';
|