ai-design-system 0.1.29 → 0.1.31

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.
@@ -0,0 +1,24 @@
1
+ import * as React from "react"
2
+ import { Shimmer } from "@/components/ai-elements/shimmer"
3
+ import { cn } from "@/lib/utils"
4
+ import type { LoadingShimmerProps } from "./interfaces"
5
+
6
+ export const LoadingShimmer = React.memo<LoadingShimmerProps>(({ message = "Loading...", className }) => {
7
+ return (
8
+ <div className={cn("flex h-full min-h-0 flex-1 items-center justify-center px-6 py-10", className)}>
9
+ <div className="w-full max-w-3xl space-y-5">
10
+ <Shimmer className="text-sm text-muted-foreground">{message}</Shimmer>
11
+ <div className="space-y-3">
12
+ <div className="h-10 w-1/3 animate-pulse rounded-md bg-muted/70" />
13
+ <div className="h-24 w-full animate-pulse rounded-xl bg-muted/60" />
14
+ <div className="grid gap-3 md:grid-cols-2">
15
+ <div className="h-36 animate-pulse rounded-xl bg-muted/55" />
16
+ <div className="h-36 animate-pulse rounded-xl bg-muted/55" />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ )
22
+ })
23
+
24
+ LoadingShimmer.displayName = "LoadingShimmer"
@@ -0,0 +1,2 @@
1
+ export { LoadingShimmer } from './LoadingShimmer'
2
+ export type { LoadingShimmerProps } from './interfaces'
@@ -0,0 +1,4 @@
1
+ export interface LoadingShimmerProps {
2
+ message?: string
3
+ className?: string
4
+ }
@@ -118,6 +118,10 @@ export * from './AdjustableLayout'
118
118
  export * from './PageContainer'
119
119
  export type { PageContainerProps } from './PageContainer'
120
120
 
121
+ // LoadingShimmer Composite
122
+ export { LoadingShimmer } from './LoadingShimmer'
123
+ export type { LoadingShimmerProps } from './LoadingShimmer'
124
+
121
125
  // StateNode Composite
122
126
  export { StateNode } from './StateNode'
123
127
  export type { StateNodeData } from './StateNode'
@@ -2,6 +2,9 @@
2
2
  * Mock data for PageLayout feature stories
3
3
  */
4
4
 
5
+ import type { FileChangeData } from '@/components/composites/FileQueue'
6
+ import type { RefinementMessage } from '@/components/features/RefinementPanel'
7
+
5
8
  export const mockSidebarConfig = {
6
9
  logo: { icon: 'command', text: 'Acme Inc.', href: '/' },
7
10
  mainNavigation: [
@@ -34,3 +37,27 @@ export const mockHeaderConfigWithTabs = {
34
37
  ],
35
38
  defaultTab: 'agent',
36
39
  }
40
+
41
+ export const mockPageLayoutRefinementMessages: RefinementMessage[] = [
42
+ {
43
+ id: '1',
44
+ type: 'human',
45
+ role: 'user',
46
+ content: 'Please optimize this workflow for better performance',
47
+ },
48
+ {
49
+ id: '2',
50
+ type: 'ai',
51
+ role: 'orchestrator',
52
+ content: "I'll analyze the workflow and suggest optimizations for better performance.",
53
+ },
54
+ ]
55
+
56
+ export const mockPageLayoutFileChanges: FileChangeData[] = [
57
+ {
58
+ id: '1',
59
+ filename: 'workflow.json',
60
+ status: 'modified',
61
+ path: 'workflow.json',
62
+ },
63
+ ]
@@ -1,11 +1,16 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
- import { useState } from 'react'
3
2
  import { PageLayout } from './PageLayout'
4
- import { usePageLayoutMock } from './usePageLayout.mock'
5
- import { mockSidebarConfig, mockHeaderConfig, mockHeaderConfigWithTabs } from './PageLayout.mocks'
6
- import { WorkflowBuilder } from '@/components/features/WorkflowBuilder'
7
- import { RefinementPanel } from '@/components/features/RefinementPanel'
8
- import { mockVersions, mockNodes, mockEdges } from '@/components/features/WorkflowBuilder/WorkflowBuilder.mocks'
3
+ import { usePageLayoutMock, usePageLayoutStoryActionsMock } from './usePageLayout.mock'
4
+ import {
5
+ mockHeaderConfig,
6
+ mockHeaderConfigWithTabs,
7
+ mockPageLayoutFileChanges,
8
+ mockPageLayoutRefinementMessages,
9
+ mockSidebarConfig,
10
+ } from './PageLayout.mocks'
11
+ import { WorkflowBuilder } from '../WorkflowBuilder/WorkflowBuilder'
12
+ import { RefinementPanel } from '../RefinementPanel/RefinementPanel'
13
+ import { mockEdges, mockNodes, mockVersions } from '../WorkflowBuilder/WorkflowBuilder.mocks'
9
14
 
10
15
  const meta = {
11
16
  title: 'Features/PageLayout',
@@ -33,8 +38,8 @@ export const Default: Story = {
33
38
  {
34
39
  id: 'file-explorer',
35
40
  content: (
36
- <div className="h-full bg-muted rounded-lg border p-4">
37
- <h3 className="font-medium mb-2">File Explorer</h3>
41
+ <div className="h-full rounded-lg border bg-muted p-4">
42
+ <h3 className="mb-2 font-medium">File Explorer</h3>
38
43
  <p className="text-sm text-muted-foreground">Project files and folders</p>
39
44
  </div>
40
45
  ),
@@ -47,8 +52,8 @@ export const Default: Story = {
47
52
  {
48
53
  id: 'editor',
49
54
  content: (
50
- <div className="h-full bg-muted rounded-lg border p-4">
51
- <h3 className="font-medium mb-2">Editor</h3>
55
+ <div className="h-full rounded-lg border bg-muted p-4">
56
+ <h3 className="mb-2 font-medium">Editor</h3>
52
57
  <p className="text-sm text-muted-foreground">Main editing area</p>
53
58
  </div>
54
59
  ),
@@ -66,8 +71,8 @@ export const Default: Story = {
66
71
  {
67
72
  id: 'properties',
68
73
  content: (
69
- <div className="h-full bg-muted rounded-lg border p-4">
70
- <h3 className="font-medium mb-2">Properties</h3>
74
+ <div className="h-full rounded-lg border bg-muted p-4">
75
+ <h3 className="mb-2 font-medium">Properties</h3>
71
76
  <p className="text-sm text-muted-foreground">File and project properties</p>
72
77
  </div>
73
78
  ),
@@ -91,40 +96,18 @@ export const Default: Story = {
91
96
  export const WithStateManagement: Story = {
92
97
  render: () => {
93
98
  const layoutState = usePageLayoutMock()
94
-
95
- // Mock data for RefinementPanel
96
- const mockMessages = [
97
- {
98
- id: '1',
99
- type: 'human' as const,
100
- role: 'user' as const,
101
- content: 'Please optimize this workflow for better performance',
102
- },
103
- {
104
- id: '2',
105
- type: 'ai' as const,
106
- role: 'orchestrator' as const,
107
- content: 'I\'ll analyze the workflow and suggest optimizations for better performance.',
108
- },
109
- ]
110
-
111
- const mockFileChanges = [
112
- {
113
- id: '1',
114
- filename: 'workflow.json',
115
- status: 'modified' as const,
116
- path: 'workflow.json',
117
- changes: '+ Added parallel processing\n+ Optimized node connections',
118
- },
119
- ]
120
-
99
+ const actions = usePageLayoutStoryActionsMock()
100
+
121
101
  return (
122
102
  <PageLayout
123
103
  sidebar={mockSidebarConfig}
124
104
  header={{
125
105
  ...mockHeaderConfigWithTabs,
106
+ defaultTab: layoutState.activeTab,
126
107
  onTabChange: layoutState.onTabChange,
127
108
  }}
109
+ isLoading={layoutState.isLoading}
110
+ loadingMessage={layoutState.loadingMessage}
128
111
  defaultSidebarOpen={layoutState.isSidebarOpen}
129
112
  layoutSections={[
130
113
  {
@@ -136,11 +119,10 @@ export const WithStateManagement: Story = {
136
119
  versions={mockVersions}
137
120
  nodes={mockNodes}
138
121
  edges={mockEdges}
139
- onVersionSelect={(id) => console.log('Selected version:', id)}
140
- onSave={() => console.log('Workflow saved')}
141
- onCancel={() => console.log('Cancelled')}
142
- onUndo={() => console.log('Undo')}
143
- onRedo={() => console.log('Redo')}
122
+ onVersionSelect={actions.onVersionSelect}
123
+ onSave={actions.onSave}
124
+ onUndo={actions.onUndo}
125
+ onRedo={actions.onRedo}
144
126
  hasUnsavedChanges={true}
145
127
  canUndo={true}
146
128
  canRedo={false}
@@ -154,13 +136,11 @@ export const WithStateManagement: Story = {
154
136
  id: 'refinement-panel',
155
137
  content: (
156
138
  <RefinementPanel
157
- messages={mockMessages}
158
- fileChanges={mockFileChanges}
159
- onSubmit={(message, event) => {
160
- console.log('Refinement request:', message)
161
- }}
162
- onApprove={() => console.log('Changes approved')}
163
- onReject={() => console.log('Changes rejected')}
139
+ messages={mockPageLayoutRefinementMessages}
140
+ fileChanges={mockPageLayoutFileChanges}
141
+ onSubmit={actions.onSubmit}
142
+ onApprove={actions.onApprove}
143
+ onReject={actions.onReject}
164
144
  placeholder="Ask for workflow optimizations or describe changes..."
165
145
  />
166
146
  ),
@@ -2,10 +2,15 @@ import * as React from "react"
2
2
  import { AppSidebar, type AppSidebarProps } from "@/components/blocks/AppSidebar"
3
3
  import { LayoutProvider } from "@/components/blocks/LayoutProvider"
4
4
  import { SectionLayout } from "@/components/blocks/SectionLayout/SectionLayout"
5
- import type { SectionLayoutProps, SectionLayoutSection } from "@/components/blocks/SectionLayout/interfaces"
5
+ import type { SectionLayoutSection } from "@/components/blocks/SectionLayout/interfaces"
6
6
  import { AppHeader, type AppHeaderProps } from "@/components/composites/AppHeader"
7
+ import { LoadingShimmer } from "@/components/composites/LoadingShimmer"
7
8
  import { PageContainer } from "@/components/composites/PageContainer"
8
9
 
10
+ function PageLayoutLoadingState({ message }: { message: string }) {
11
+ return <LoadingShimmer message={message} />
12
+ }
13
+
9
14
  /**
10
15
  * PageLayout Feature
11
16
  *
@@ -56,6 +61,24 @@ export interface PageLayoutProps {
56
61
  * Optional when layoutSections is provided
57
62
  */
58
63
  children?: React.ReactNode
64
+ /**
65
+ * Whether the page content should render a built-in loading state.
66
+ */
67
+ isLoading?: boolean
68
+ /**
69
+ * Optional loading message shown inside the built-in shimmer state.
70
+ * @default "Loading..."
71
+ */
72
+ loadingMessage?: string
73
+ /**
74
+ * Optional custom shimmer/loading UI provided by the consumer.
75
+ * When omitted, PageLayout renders its built-in default shimmer state.
76
+ */
77
+ loadingShimmer?: React.ReactNode
78
+ /**
79
+ * Custom loading fallback. When provided, this replaces the built-in shimmer layout.
80
+ */
81
+ loadingFallback?: React.ReactNode
59
82
  /**
60
83
  * Additional CSS classes
61
84
  */
@@ -107,6 +130,10 @@ export const PageLayout = React.memo<PageLayoutProps>(
107
130
  sidebar,
108
131
  header,
109
132
  children,
133
+ isLoading = false,
134
+ loadingMessage = "Loading...",
135
+ loadingShimmer,
136
+ loadingFallback,
110
137
  className,
111
138
  defaultSidebarOpen = true,
112
139
  sidebarWidth = "var(--spacing-sidebar-width)",
@@ -116,17 +143,19 @@ export const PageLayout = React.memo<PageLayoutProps>(
116
143
  layoutStorageKey,
117
144
  dragHandleColor = "border",
118
145
  }) => {
119
- const contentArea = layoutSections ? (
120
- <SectionLayout
121
- sections={layoutSections}
122
- orientation={layoutOrientation}
123
- storageKey={layoutStorageKey}
124
- dragHandleColor={dragHandleColor}
125
- className="flex-1 min-h-0"
126
- />
127
- ) : (
128
- <div className="flex min-h-0 flex-1 flex-col">{children}</div>
129
- )
146
+ const contentArea = isLoading
147
+ ? (loadingShimmer ?? loadingFallback ?? <PageLayoutLoadingState message={loadingMessage} />)
148
+ : layoutSections ? (
149
+ <SectionLayout
150
+ sections={layoutSections}
151
+ orientation={layoutOrientation}
152
+ storageKey={layoutStorageKey}
153
+ dragHandleColor={dragHandleColor}
154
+ className="flex-1 min-h-0"
155
+ />
156
+ ) : (
157
+ <div className="flex min-h-0 flex-1 flex-col">{children}</div>
158
+ )
130
159
 
131
160
  const pageContainer = (
132
161
  <PageContainer className={`overflow-hidden ${className ?? ""}`}>
@@ -137,17 +166,13 @@ export const PageLayout = React.memo<PageLayoutProps>(
137
166
  </PageContainer>
138
167
  )
139
168
 
140
- if (!sidebar) {
141
- return pageContainer
142
- }
143
-
144
169
  return (
145
170
  <LayoutProvider
146
171
  defaultOpen={defaultSidebarOpen}
147
172
  sidebarWidth={sidebarWidth}
148
173
  sidebarWidthIcon={sidebarWidthIcon}
149
174
  >
150
- <AppSidebar {...sidebar} />
175
+ {sidebar ? <AppSidebar {...sidebar} /> : null}
151
176
  {pageContainer}
152
177
  </LayoutProvider>
153
178
  )
@@ -12,6 +12,10 @@ export interface UsePageLayoutReturn {
12
12
  isSidebarOpen: boolean
13
13
  /** Currently active tab value */
14
14
  activeTab: string
15
+ /** Whether a page-level loading state is active */
16
+ isLoading: boolean
17
+ /** Optional message for the page-level loading state */
18
+ loadingMessage?: string
15
19
  /** Toggle sidebar open/closed */
16
20
  toggleSidebar: () => void
17
21
  /** Set sidebar open state */
@@ -1,4 +1,6 @@
1
- import { useState, useCallback } from 'react'
1
+ import { useState, useCallback, useEffect, useRef } from 'react'
2
+ import type { FormEvent } from 'react'
3
+ import type { PromptInputMessage } from '@/components/ai-elements/prompt-input'
2
4
  import type { UsePageLayoutReturn } from './usePageLayout.d'
3
5
 
4
6
  /**
@@ -7,15 +9,36 @@ import type { UsePageLayoutReturn } from './usePageLayout.d'
7
9
  export const usePageLayoutMock = (): UsePageLayoutReturn => {
8
10
  const [isSidebarOpen, setIsSidebarOpen] = useState(true)
9
11
  const [activeTab, setActiveTab] = useState('agent')
12
+ const [isLoading, setIsLoading] = useState(false)
13
+ const loadingTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
14
+
15
+ useEffect(() => {
16
+ return () => {
17
+ if (loadingTimerRef.current) {
18
+ clearTimeout(loadingTimerRef.current)
19
+ }
20
+ }
21
+ }, [])
10
22
 
11
23
  const handleTabChange = useCallback((value: string) => {
12
24
  console.log('🔄 State Management - Tab switched to:', value)
13
25
  setActiveTab(value)
26
+ setIsLoading(true)
27
+
28
+ if (loadingTimerRef.current) {
29
+ clearTimeout(loadingTimerRef.current)
30
+ }
31
+
32
+ loadingTimerRef.current = setTimeout(() => {
33
+ setIsLoading(false)
34
+ }, 900)
14
35
  }, [])
15
36
 
16
37
  return {
17
38
  isSidebarOpen,
18
39
  activeTab,
40
+ isLoading,
41
+ loadingMessage: `Loading ${activeTab} view...`,
19
42
  toggleSidebar: () => {
20
43
  const newState = !isSidebarOpen
21
44
  console.log('🔄 State Management - Sidebar toggled to:', newState)
@@ -32,3 +55,43 @@ export const usePageLayoutMock = (): UsePageLayoutReturn => {
32
55
  },
33
56
  }
34
57
  }
58
+
59
+ export function usePageLayoutStoryActionsMock() {
60
+ const onVersionSelect = useCallback((id: string) => {
61
+ console.log('🔄 State Management - Selected version:', id)
62
+ }, [])
63
+
64
+ const onSave = useCallback(() => {
65
+ console.log('🔄 State Management - Workflow saved')
66
+ }, [])
67
+
68
+ const onUndo = useCallback(() => {
69
+ console.log('🔄 State Management - Undo')
70
+ }, [])
71
+
72
+ const onRedo = useCallback(() => {
73
+ console.log('🔄 State Management - Redo')
74
+ }, [])
75
+
76
+ const onSubmit = useCallback((message: PromptInputMessage, _event: FormEvent<HTMLFormElement>) => {
77
+ console.log('🔄 State Management - Refinement request:', message)
78
+ }, [])
79
+
80
+ const onApprove = useCallback(() => {
81
+ console.log('🔄 State Management - Changes approved')
82
+ }, [])
83
+
84
+ const onReject = useCallback(() => {
85
+ console.log('🔄 State Management - Changes rejected')
86
+ }, [])
87
+
88
+ return {
89
+ onVersionSelect,
90
+ onSave,
91
+ onUndo,
92
+ onRedo,
93
+ onSubmit,
94
+ onApprove,
95
+ onReject,
96
+ }
97
+ }
@@ -110,7 +110,7 @@ export const MultiAgentReviewState: Story = {
110
110
  export const WithStateManagement: Story = {
111
111
  render: () => {
112
112
  // Use mocked hook for state management
113
- const { messages, fileChanges, handleSubmit, handleApprove, handleReject } =
113
+ const { messages, fileChanges, onSubmit, handleApprove, handleReject } =
114
114
  useRefinementPanelMock({
115
115
  initialMessages: inputStateMessages,
116
116
  reviewMessages: reviewStateMessages,
@@ -123,7 +123,7 @@ export const WithStateManagement: Story = {
123
123
  messages={messages}
124
124
  fileChanges={fileChanges}
125
125
  placeholder="Ask a question or describe a task..."
126
- onSubmit={(message) => handleSubmit(message.text)}
126
+ onSubmit={onSubmit}
127
127
  onApprove={handleApprove}
128
128
  onReject={handleReject}
129
129
  />
@@ -1,6 +1,8 @@
1
1
  import { useState, useCallback } from "react";
2
2
  import type { RefinementMessage } from "./RefinementPanel";
3
3
  import type { FileChangeData } from "@/components/composites/FileQueue";
4
+ import type { PromptInputMessage } from "@/components/ai-elements/prompt-input";
5
+ import type { FormEvent } from "react";
4
6
 
5
7
  /**
6
8
  * Mock hook for RefinementPanel state management
@@ -18,6 +20,7 @@ export interface UseRefinementPanelReturn {
18
20
  fileChanges: FileChangeData[];
19
21
  loading: boolean;
20
22
  handleSubmit: (prompt: string) => Promise<void>;
23
+ onSubmit: (message: PromptInputMessage, event: FormEvent<HTMLFormElement>) => Promise<void>;
21
24
  handleApprove: () => Promise<void>;
22
25
  handleReject: () => Promise<void>;
23
26
  }
@@ -62,6 +65,10 @@ export function useRefinementPanelMock(
62
65
  setLoading(false);
63
66
  }, [reviewMessages, reviewFileChanges, apiDelay]);
64
67
 
68
+ const onSubmit = useCallback(async (message: PromptInputMessage, _event: FormEvent<HTMLFormElement>) => {
69
+ await handleSubmit(message.text);
70
+ }, [handleSubmit]);
71
+
65
72
  // Simulate approval: clear file changes and add success message
66
73
  const handleApprove = useCallback(async () => {
67
74
  setLoading(true);
@@ -115,6 +122,7 @@ export function useRefinementPanelMock(
115
122
  fileChanges,
116
123
  loading,
117
124
  handleSubmit,
125
+ onSubmit,
118
126
  handleApprove,
119
127
  handleReject,
120
128
  };
package/dist/index.cjs CHANGED
@@ -12,9 +12,9 @@ var AvatarPrimitive = require('@radix-ui/react-avatar');
12
12
  var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
13
13
  var lucideReact = require('lucide-react');
14
14
  var TabsPrimitive = require('@radix-ui/react-tabs');
15
+ var react$1 = require('motion/react');
15
16
  var useStickToBottom = require('use-stick-to-bottom');
16
17
  var CollapsiblePrimitive = require('@radix-ui/react-collapsible');
17
- var react$1 = require('motion/react');
18
18
  var shiki = require('shiki');
19
19
  var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
20
20
  var cmdk = require('cmdk');
@@ -1667,15 +1667,73 @@ var SectionLayout = React3__namespace.memo(
1667
1667
  }
1668
1668
  );
1669
1669
  SectionLayout.displayName = "SectionLayout";
1670
+ var ShimmerComponent = ({
1671
+ children,
1672
+ className,
1673
+ duration = 2,
1674
+ spread = 2
1675
+ }) => {
1676
+ const dynamicSpread = React3.useMemo(
1677
+ () => {
1678
+ var _a;
1679
+ return ((_a = children == null ? void 0 : children.length) != null ? _a : 0) * spread;
1680
+ },
1681
+ [children, spread]
1682
+ );
1683
+ return /* @__PURE__ */ jsxRuntime.jsx(
1684
+ react$1.motion.p,
1685
+ {
1686
+ animate: { backgroundPosition: "0% center" },
1687
+ className: cn(
1688
+ "relative inline-block bg-size-[250%_100%,auto] bg-clip-text text-transparent",
1689
+ "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
1690
+ className
1691
+ ),
1692
+ initial: { backgroundPosition: "100% center" },
1693
+ style: {
1694
+ "--spread": `${dynamicSpread}px`,
1695
+ backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
1696
+ },
1697
+ transition: {
1698
+ repeat: Number.POSITIVE_INFINITY,
1699
+ duration,
1700
+ ease: "linear"
1701
+ },
1702
+ children
1703
+ }
1704
+ );
1705
+ };
1706
+ var Shimmer = React3.memo(ShimmerComponent);
1707
+ var LoadingShimmer = React3__namespace.memo(({ message = "Loading...", className }) => {
1708
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex h-full min-h-0 flex-1 items-center justify-center px-6 py-10", className), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-3xl space-y-5", children: [
1709
+ /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { className: "text-sm text-muted-foreground", children: message }),
1710
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
1711
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10 w-1/3 animate-pulse rounded-md bg-muted/70" }),
1712
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-24 w-full animate-pulse rounded-xl bg-muted/60" }),
1713
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [
1714
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-36 animate-pulse rounded-xl bg-muted/55" }),
1715
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-36 animate-pulse rounded-xl bg-muted/55" })
1716
+ ] })
1717
+ ] })
1718
+ ] }) });
1719
+ });
1720
+ LoadingShimmer.displayName = "LoadingShimmer";
1670
1721
  var PageContainer = React3__namespace.memo(({ children, className }) => {
1671
1722
  return /* @__PURE__ */ jsxRuntime.jsx(SidebarInset2, { className, children });
1672
1723
  });
1673
1724
  PageContainer.displayName = "PageContainer";
1725
+ function PageLayoutLoadingState({ message }) {
1726
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingShimmer, { message });
1727
+ }
1674
1728
  var PageLayout = React3__namespace.memo(
1675
1729
  ({
1676
1730
  sidebar,
1677
1731
  header,
1678
1732
  children,
1733
+ isLoading = false,
1734
+ loadingMessage = "Loading...",
1735
+ loadingShimmer,
1736
+ loadingFallback,
1679
1737
  className,
1680
1738
  defaultSidebarOpen = true,
1681
1739
  sidebarWidth = "var(--spacing-sidebar-width)",
@@ -1685,7 +1743,8 @@ var PageLayout = React3__namespace.memo(
1685
1743
  layoutStorageKey,
1686
1744
  dragHandleColor = "border"
1687
1745
  }) => {
1688
- const contentArea = layoutSections ? /* @__PURE__ */ jsxRuntime.jsx(
1746
+ var _a;
1747
+ const contentArea = isLoading ? (_a = loadingShimmer != null ? loadingShimmer : loadingFallback) != null ? _a : /* @__PURE__ */ jsxRuntime.jsx(PageLayoutLoadingState, { message: loadingMessage }) : layoutSections ? /* @__PURE__ */ jsxRuntime.jsx(
1689
1748
  SectionLayout,
1690
1749
  {
1691
1750
  sections: layoutSections,
@@ -1699,9 +1758,6 @@ var PageLayout = React3__namespace.memo(
1699
1758
  /* @__PURE__ */ jsxRuntime.jsx(AppHeader, __spreadValues({}, header)),
1700
1759
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `min-h-0 flex-1 overflow-x-hidden ${layoutSections ? "overflow-hidden" : "overflow-y-auto"}`, children: contentArea })
1701
1760
  ] });
1702
- if (!sidebar) {
1703
- return pageContainer;
1704
- }
1705
1761
  return /* @__PURE__ */ jsxRuntime.jsxs(
1706
1762
  LayoutProvider,
1707
1763
  {
@@ -1709,7 +1765,7 @@ var PageLayout = React3__namespace.memo(
1709
1765
  sidebarWidth,
1710
1766
  sidebarWidthIcon,
1711
1767
  children: [
1712
- /* @__PURE__ */ jsxRuntime.jsx(AppSidebar, __spreadValues({}, sidebar)),
1768
+ sidebar ? /* @__PURE__ */ jsxRuntime.jsx(AppSidebar, __spreadValues({}, sidebar)) : null,
1713
1769
  pageContainer
1714
1770
  ]
1715
1771
  }
@@ -1997,43 +2053,6 @@ function CollapsibleContent2(_a) {
1997
2053
  }, props)
1998
2054
  );
1999
2055
  }
2000
- var ShimmerComponent = ({
2001
- children,
2002
- className,
2003
- duration = 2,
2004
- spread = 2
2005
- }) => {
2006
- const dynamicSpread = React3.useMemo(
2007
- () => {
2008
- var _a;
2009
- return ((_a = children == null ? void 0 : children.length) != null ? _a : 0) * spread;
2010
- },
2011
- [children, spread]
2012
- );
2013
- return /* @__PURE__ */ jsxRuntime.jsx(
2014
- react$1.motion.p,
2015
- {
2016
- animate: { backgroundPosition: "0% center" },
2017
- className: cn(
2018
- "relative inline-block bg-size-[250%_100%,auto] bg-clip-text text-transparent",
2019
- "[--bg:linear-gradient(90deg,#0000_calc(50%-var(--spread)),var(--color-background),#0000_calc(50%+var(--spread)))] [background-repeat:no-repeat,padding-box]",
2020
- className
2021
- ),
2022
- initial: { backgroundPosition: "100% center" },
2023
- style: {
2024
- "--spread": `${dynamicSpread}px`,
2025
- backgroundImage: "var(--bg), linear-gradient(var(--color-muted-foreground), var(--color-muted-foreground))"
2026
- },
2027
- transition: {
2028
- repeat: Number.POSITIVE_INFINITY,
2029
- duration,
2030
- ease: "linear"
2031
- },
2032
- children
2033
- }
2034
- );
2035
- };
2036
- var Shimmer = React3.memo(ShimmerComponent);
2037
2056
  var PlanContext = React3.createContext(null);
2038
2057
  var usePlan = () => {
2039
2058
  const context = React3.useContext(PlanContext);