@contractspec/lib.example-shared-ui 6.0.5 → 6.0.7
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/.turbo/turbo-build.log +90 -84
- package/AGENTS.md +43 -25
- package/CHANGELOG.md +11 -0
- package/README.md +63 -35
- package/dist/EvolutionDashboard.js +9 -9
- package/dist/EvolutionSidebar.js +15 -15
- package/dist/LocalDataIndicator.js +3 -3
- package/dist/MarkdownView.d.ts +0 -7
- package/dist/MarkdownView.js +76 -172
- package/dist/PersonalizationInsights.js +12 -12
- package/dist/SaveToStudioButton.js +2 -2
- package/dist/SpecDrivenTemplateShell.d.ts +1 -1
- package/dist/SpecDrivenTemplateShell.js +10 -10
- package/dist/SpecEditorPanel.js +3 -3
- package/dist/TemplateShell.js +10 -10
- package/dist/browser/EvolutionDashboard.js +9 -9
- package/dist/browser/EvolutionSidebar.js +15 -15
- package/dist/browser/LocalDataIndicator.js +3 -3
- package/dist/browser/MarkdownView.js +76 -172
- package/dist/browser/PersonalizationInsights.js +12 -12
- package/dist/browser/SaveToStudioButton.js +2 -2
- package/dist/browser/SpecDrivenTemplateShell.js +10 -10
- package/dist/browser/SpecEditorPanel.js +3 -3
- package/dist/browser/TemplateShell.js +10 -10
- package/dist/browser/hooks/index.js +29 -29
- package/dist/browser/index.js +193 -286
- package/dist/browser/lib/component-registry.js +1 -1
- package/dist/browser/markdown/formatPresentationName.js +9 -0
- package/dist/browser/markdown/useMarkdownPresentation.js +65 -0
- package/dist/hooks/index.d.ts +3 -3
- package/dist/hooks/index.js +29 -29
- package/dist/index.d.ts +12 -11
- package/dist/index.js +193 -286
- package/dist/lib/component-registry.js +1 -1
- package/dist/markdown/formatPresentationName.d.ts +1 -0
- package/dist/markdown/formatPresentationName.js +10 -0
- package/dist/markdown/useMarkdownPresentation.d.ts +21 -0
- package/dist/markdown/useMarkdownPresentation.js +66 -0
- package/dist/node/EvolutionDashboard.js +9 -9
- package/dist/node/EvolutionSidebar.js +15 -15
- package/dist/node/LocalDataIndicator.js +3 -3
- package/dist/node/MarkdownView.js +76 -172
- package/dist/node/PersonalizationInsights.js +12 -12
- package/dist/node/SaveToStudioButton.js +2 -2
- package/dist/node/SpecDrivenTemplateShell.js +10 -10
- package/dist/node/SpecEditorPanel.js +3 -3
- package/dist/node/TemplateShell.js +10 -10
- package/dist/node/hooks/index.js +29 -29
- package/dist/node/index.js +193 -286
- package/dist/node/lib/component-registry.js +1 -1
- package/dist/node/markdown/formatPresentationName.js +9 -0
- package/dist/node/markdown/useMarkdownPresentation.js +65 -0
- package/dist/utils/index.d.ts +1 -1
- package/package.json +40 -13
- package/src/EvolutionDashboard.tsx +415 -415
- package/src/EvolutionSidebar.tsx +245 -245
- package/src/LocalDataIndicator.tsx +28 -28
- package/src/MarkdownView.tsx +119 -372
- package/src/OverlayContextProvider.tsx +272 -272
- package/src/PersonalizationInsights.tsx +232 -232
- package/src/SaveToStudioButton.tsx +51 -51
- package/src/SpecDrivenTemplateShell.tsx +59 -59
- package/src/SpecEditorPanel.tsx +138 -138
- package/src/TemplateShell.tsx +50 -50
- package/src/bundles/ExampleTemplateBundle.ts +78 -78
- package/src/hooks/index.ts +3 -3
- package/src/hooks/useBehaviorTracking.ts +252 -252
- package/src/hooks/useEvolution.ts +437 -437
- package/src/hooks/useRegistryTemplates.ts +42 -42
- package/src/hooks/useSpecContent.ts +214 -214
- package/src/hooks/useWorkflowComposer.ts +567 -567
- package/src/index.ts +12 -11
- package/src/lib/component-registry.tsx +40 -40
- package/src/lib/runtime-context.tsx +31 -31
- package/src/lib/types.ts +57 -57
- package/src/markdown/formatPresentationName.ts +9 -0
- package/src/markdown/useMarkdownPresentation.ts +107 -0
- package/src/overlay-types.ts +15 -15
- package/src/utils/fetchPresentationData.ts +13 -13
- package/src/utils/generateSpecFromTemplate.ts +29 -29
- package/src/utils/index.ts +1 -1
- package/tsconfig.json +8 -8
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
BundleProvider,
|
|
3
|
+
BundleRenderer,
|
|
5
4
|
} from '@contractspec/lib.surface-runtime/react';
|
|
6
5
|
import type { ResolvedSurfacePlan } from '@contractspec/lib.surface-runtime/runtime/resolve-bundle';
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
7
|
import { LocalDataIndicator } from './LocalDataIndicator';
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
SaveToStudioButton,
|
|
10
|
+
type SaveToStudioButtonProps,
|
|
11
11
|
} from './SaveToStudioButton';
|
|
12
12
|
|
|
13
13
|
export interface SpecDrivenTemplateShellProps {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
plan: ResolvedSurfacePlan;
|
|
15
|
+
title: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
sidebar?: ReactNode;
|
|
18
|
+
actions?: ReactNode;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
showSaveAction?: boolean;
|
|
21
|
+
saveProps?: SaveToStudioButtonProps;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -27,53 +27,53 @@ export interface SpecDrivenTemplateShellProps {
|
|
|
27
27
|
* Requires @contractspec/lib.surface-runtime as peer dependency.
|
|
28
28
|
*/
|
|
29
29
|
export function SpecDrivenTemplateShell({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
plan,
|
|
31
|
+
title,
|
|
32
|
+
description,
|
|
33
|
+
sidebar,
|
|
34
|
+
actions,
|
|
35
|
+
showSaveAction = true,
|
|
36
|
+
saveProps,
|
|
37
|
+
children,
|
|
38
38
|
}: SpecDrivenTemplateShellProps) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
39
|
+
const headerContent = (
|
|
40
|
+
<header className="rounded-2xl border border-border bg-card p-6 shadow-sm">
|
|
41
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
42
|
+
<div>
|
|
43
|
+
<p className="font-semibold text-muted-foreground text-sm uppercase tracking-wide">
|
|
44
|
+
ContractSpec Templates
|
|
45
|
+
</p>
|
|
46
|
+
<h1 className="font-bold text-3xl">{title}</h1>
|
|
47
|
+
{description ? (
|
|
48
|
+
<p className="mt-2 max-w-2xl text-muted-foreground text-sm">
|
|
49
|
+
{description}
|
|
50
|
+
</p>
|
|
51
|
+
) : null}
|
|
52
|
+
</div>
|
|
53
|
+
<div className="flex flex-col items-end gap-2">
|
|
54
|
+
<LocalDataIndicator />
|
|
55
|
+
{showSaveAction ? <SaveToStudioButton {...saveProps} /> : null}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
{actions ? <div className="mt-4">{actions}</div> : null}
|
|
59
|
+
</header>
|
|
60
|
+
);
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
const slotContent: Partial<Record<string, ReactNode>> = {
|
|
63
|
+
header: headerContent,
|
|
64
|
+
primary: <main className="space-y-4 p-2">{children}</main>,
|
|
65
|
+
};
|
|
66
|
+
if (sidebar != null) {
|
|
67
|
+
slotContent.sidebar = (
|
|
68
|
+
<aside className="rounded-2xl border border-border bg-card p-4">
|
|
69
|
+
{sidebar}
|
|
70
|
+
</aside>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
return (
|
|
75
|
+
<BundleProvider plan={plan}>
|
|
76
|
+
<BundleRenderer slotContent={slotContent} />
|
|
77
|
+
</BundleProvider>
|
|
78
|
+
);
|
|
79
79
|
}
|
package/src/SpecEditorPanel.tsx
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useCallback, useEffect } from 'react';
|
|
4
3
|
import { Button, LoaderBlock } from '@contractspec/lib.design-system';
|
|
5
4
|
import { Badge } from '@contractspec/lib.ui-kit-web/ui/badge';
|
|
6
|
-
import
|
|
5
|
+
import { useCallback, useEffect } from 'react';
|
|
7
6
|
import { useSpecContent } from './hooks/useSpecContent';
|
|
7
|
+
import type { TemplateId } from './lib/types';
|
|
8
8
|
|
|
9
9
|
export interface SpecEditorProps {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
projectId: string;
|
|
11
|
+
type?: 'CAPABILITY' | 'DATAVIEW' | 'WORKFLOW' | 'POLICY' | 'COMPONENT';
|
|
12
|
+
content: string;
|
|
13
|
+
onChange: (content: string) => void;
|
|
14
|
+
metadata?: Record<string, unknown>;
|
|
15
|
+
onSave?: () => void;
|
|
16
|
+
onValidate?: () => void;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export interface SpecEditorPanelProps {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
templateId: TemplateId;
|
|
21
|
+
/** SpecEditor component passed as a prop (for dynamic import compatibility) */
|
|
22
|
+
SpecEditor: React.ComponentType<SpecEditorProps>;
|
|
23
|
+
/** Callback for logging actions */
|
|
24
|
+
onLog?: (message: string) => void;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -29,137 +29,137 @@ export interface SpecEditorPanelProps {
|
|
|
29
29
|
* Uses useSpecContent hook to manage spec persistence and validation.
|
|
30
30
|
*/
|
|
31
31
|
export function SpecEditorPanel({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
templateId,
|
|
33
|
+
SpecEditor,
|
|
34
|
+
onLog,
|
|
35
35
|
}: SpecEditorPanelProps) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
const {
|
|
37
|
+
content,
|
|
38
|
+
loading,
|
|
39
|
+
isDirty,
|
|
40
|
+
validation,
|
|
41
|
+
setContent,
|
|
42
|
+
save,
|
|
43
|
+
validate,
|
|
44
|
+
reset,
|
|
45
|
+
lastSaved,
|
|
46
|
+
} = useSpecContent(templateId);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
// Log when spec is loaded
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!loading && content) {
|
|
51
|
+
onLog?.(`Spec loaded for ${templateId}`);
|
|
52
|
+
}
|
|
53
|
+
}, [loading, content, templateId, onLog]);
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const handleSave = useCallback(() => {
|
|
56
|
+
save();
|
|
57
|
+
onLog?.('Spec saved locally');
|
|
58
|
+
}, [save, onLog]);
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
const handleValidate = useCallback(() => {
|
|
61
|
+
const result = validate();
|
|
62
|
+
if (result.valid) {
|
|
63
|
+
onLog?.('Spec validation passed');
|
|
64
|
+
} else {
|
|
65
|
+
const errorCount = result.errors.filter(
|
|
66
|
+
(e) => e.severity === 'error'
|
|
67
|
+
).length;
|
|
68
|
+
const warnCount = result.errors.filter(
|
|
69
|
+
(e) => e.severity === 'warning'
|
|
70
|
+
).length;
|
|
71
|
+
onLog?.(`Spec validation: ${errorCount} errors, ${warnCount} warnings`);
|
|
72
|
+
}
|
|
73
|
+
}, [validate, onLog]);
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const handleReset = useCallback(() => {
|
|
76
|
+
reset();
|
|
77
|
+
onLog?.('Spec reset to template defaults');
|
|
78
|
+
}, [reset, onLog]);
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
if (loading) {
|
|
81
|
+
return <LoaderBlock label="Loading spec..." />;
|
|
82
|
+
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
84
|
+
return (
|
|
85
|
+
<div className="space-y-4">
|
|
86
|
+
{/* Spec Toolbar */}
|
|
87
|
+
<div className="flex items-center justify-between">
|
|
88
|
+
<div className="flex items-center gap-2">
|
|
89
|
+
<Button variant="default" size="sm" onClick={handleSave}>
|
|
90
|
+
Save
|
|
91
|
+
</Button>
|
|
92
|
+
<Button variant="outline" size="sm" onClick={handleValidate}>
|
|
93
|
+
Validate
|
|
94
|
+
</Button>
|
|
95
|
+
{isDirty && (
|
|
96
|
+
<Badge
|
|
97
|
+
variant="secondary"
|
|
98
|
+
className="border-amber-500/30 bg-amber-500/20 text-amber-400"
|
|
99
|
+
>
|
|
100
|
+
Unsaved changes
|
|
101
|
+
</Badge>
|
|
102
|
+
)}
|
|
103
|
+
{validation && (
|
|
104
|
+
<Badge
|
|
105
|
+
variant={validation.valid ? 'default' : 'destructive'}
|
|
106
|
+
className={
|
|
107
|
+
validation.valid
|
|
108
|
+
? 'border-green-500/30 bg-green-500/20 text-green-400'
|
|
109
|
+
: ''
|
|
110
|
+
}
|
|
111
|
+
>
|
|
112
|
+
{validation.valid
|
|
113
|
+
? 'Valid'
|
|
114
|
+
: `${validation.errors.filter((e) => e.severity === 'error').length} errors`}
|
|
115
|
+
</Badge>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
<div className="flex items-center gap-2">
|
|
119
|
+
{lastSaved && (
|
|
120
|
+
<span className="text-muted-foreground text-xs">
|
|
121
|
+
Last saved: {new Date(lastSaved).toLocaleTimeString()}
|
|
122
|
+
</span>
|
|
123
|
+
)}
|
|
124
|
+
<Button variant="ghost" size="sm" onPress={handleReset}>
|
|
125
|
+
Reset
|
|
126
|
+
</Button>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
130
|
+
{/* Validation Errors */}
|
|
131
|
+
{validation && validation.errors.length > 0 && (
|
|
132
|
+
<div className="rounded-lg border border-amber-500/50 bg-amber-500/10 p-3">
|
|
133
|
+
<p className="mb-2 font-semibold text-amber-400 text-xs uppercase">
|
|
134
|
+
Validation Issues
|
|
135
|
+
</p>
|
|
136
|
+
<ul className="space-y-1">
|
|
137
|
+
{validation.errors.map((error, index) => (
|
|
138
|
+
<li
|
|
139
|
+
key={`${error.line}-${error.message}-${index}`}
|
|
140
|
+
className={`text-xs ${
|
|
141
|
+
error.severity === 'error' ? 'text-red-400' : 'text-amber-400'
|
|
142
|
+
}`}
|
|
143
|
+
>
|
|
144
|
+
Line {error.line}: {error.message}
|
|
145
|
+
</li>
|
|
146
|
+
))}
|
|
147
|
+
</ul>
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
151
|
+
{/* Editor */}
|
|
152
|
+
<div className="rounded-2xl border border-border bg-card p-4">
|
|
153
|
+
<SpecEditor
|
|
154
|
+
projectId="sandbox"
|
|
155
|
+
type="CAPABILITY"
|
|
156
|
+
content={content}
|
|
157
|
+
onChange={setContent}
|
|
158
|
+
metadata={{ template: templateId }}
|
|
159
|
+
onSave={handleSave}
|
|
160
|
+
onValidate={handleValidate}
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
165
|
}
|
package/src/TemplateShell.tsx
CHANGED
|
@@ -2,62 +2,62 @@ import type { ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { LocalDataIndicator } from './LocalDataIndicator';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
SaveToStudioButton,
|
|
6
|
+
type SaveToStudioButtonProps,
|
|
7
7
|
} from './SaveToStudioButton';
|
|
8
8
|
|
|
9
9
|
export interface TemplateShellProps {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
sidebar?: ReactNode;
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
showSaveAction?: boolean;
|
|
16
|
+
saveProps?: SaveToStudioButtonProps;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const TemplateShell = ({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
title,
|
|
21
|
+
description,
|
|
22
|
+
sidebar,
|
|
23
|
+
actions,
|
|
24
|
+
showSaveAction = true,
|
|
25
|
+
saveProps,
|
|
26
|
+
children,
|
|
27
27
|
}: TemplateShellProps) => (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
<div className="space-y-6">
|
|
29
|
+
<header className="rounded-2xl border border-border bg-card p-6 shadow-sm">
|
|
30
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
31
|
+
<div>
|
|
32
|
+
<p className="font-semibold text-muted-foreground text-sm uppercase tracking-wide">
|
|
33
|
+
ContractSpec Templates
|
|
34
|
+
</p>
|
|
35
|
+
<h1 className="font-bold text-3xl">{title}</h1>
|
|
36
|
+
{description ? (
|
|
37
|
+
<p className="mt-2 max-w-2xl text-muted-foreground text-sm">
|
|
38
|
+
{description}
|
|
39
|
+
</p>
|
|
40
|
+
) : null}
|
|
41
|
+
</div>
|
|
42
|
+
<div className="flex flex-col items-end gap-2">
|
|
43
|
+
<LocalDataIndicator />
|
|
44
|
+
{showSaveAction ? <SaveToStudioButton {...saveProps} /> : null}
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
{actions ? <div className="mt-4">{actions}</div> : null}
|
|
48
|
+
</header>
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
50
|
+
<div
|
|
51
|
+
className={
|
|
52
|
+
sidebar ? 'grid gap-6 lg:grid-cols-[minmax(0,1fr)_320px]' : 'w-full'
|
|
53
|
+
}
|
|
54
|
+
>
|
|
55
|
+
<main className="space-y-4 p-2">{children}</main>
|
|
56
|
+
{sidebar ? (
|
|
57
|
+
<aside className="rounded-2xl border border-border bg-card p-4">
|
|
58
|
+
{sidebar}
|
|
59
|
+
</aside>
|
|
60
|
+
) : null}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
63
|
);
|