@sero-ai/ui 0.3.2 → 0.4.0

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 (190) hide show
  1. package/.turbo/turbo-build.log +628 -0
  2. package/.turbo/turbo-typecheck.log +1 -1
  3. package/CHANGELOG.md +38 -0
  4. package/dist/components/context-editor/CapabilitySection.cjs +57 -0
  5. package/dist/components/context-editor/CapabilitySection.d.cts +20 -0
  6. package/dist/components/context-editor/CapabilitySection.d.ts +20 -0
  7. package/dist/components/context-editor/CapabilitySection.js +57 -0
  8. package/dist/components/context-editor/ContextEditor.cjs +108 -0
  9. package/dist/components/context-editor/ContextEditor.d.cts +26 -0
  10. package/dist/components/context-editor/ContextEditor.d.ts +26 -0
  11. package/dist/components/context-editor/ContextEditor.js +108 -0
  12. package/dist/components/context-editor/PresetBar.cjs +71 -0
  13. package/dist/components/context-editor/PresetBar.d.cts +19 -0
  14. package/dist/components/context-editor/PresetBar.d.ts +19 -0
  15. package/dist/components/context-editor/PresetBar.js +71 -0
  16. package/dist/components/context-editor/SkillsSection.cjs +31 -0
  17. package/dist/components/context-editor/SkillsSection.d.cts +14 -0
  18. package/dist/components/context-editor/SkillsSection.d.ts +14 -0
  19. package/dist/components/context-editor/SkillsSection.js +31 -0
  20. package/dist/components/context-editor/SystemPromptSection.cjs +58 -0
  21. package/dist/components/context-editor/SystemPromptSection.d.cts +25 -0
  22. package/dist/components/context-editor/SystemPromptSection.d.ts +25 -0
  23. package/dist/components/context-editor/SystemPromptSection.js +58 -0
  24. package/dist/components/context-editor/ToolsSection.cjs +31 -0
  25. package/dist/components/context-editor/ToolsSection.d.cts +14 -0
  26. package/dist/components/context-editor/ToolsSection.d.ts +14 -0
  27. package/dist/components/context-editor/ToolsSection.js +31 -0
  28. package/dist/components/context-editor/index.cjs +8 -0
  29. package/dist/components/context-editor/index.d.cts +5 -0
  30. package/dist/components/context-editor/index.d.ts +5 -0
  31. package/dist/components/context-editor/index.js +8 -0
  32. package/dist/components/context-editor/parts.cjs +184 -0
  33. package/dist/components/context-editor/parts.d.cts +32 -0
  34. package/dist/components/context-editor/parts.d.ts +32 -0
  35. package/dist/components/context-editor/parts.js +184 -0
  36. package/dist/components/context-editor/use-context-editor-model.cjs +194 -0
  37. package/dist/components/context-editor/use-context-editor-model.d.cts +57 -0
  38. package/dist/components/context-editor/use-context-editor-model.d.ts +57 -0
  39. package/dist/components/context-editor/use-context-editor-model.js +194 -0
  40. package/dist/components/dashboard/activity-list.cjs +59 -0
  41. package/dist/components/dashboard/activity-list.d.cts +27 -0
  42. package/dist/components/dashboard/activity-list.d.ts +27 -0
  43. package/dist/components/dashboard/activity-list.js +59 -0
  44. package/dist/components/dashboard/catalog.cjs +322 -0
  45. package/dist/components/dashboard/catalog.d.cts +14 -0
  46. package/dist/components/dashboard/catalog.d.ts +14 -0
  47. package/dist/components/dashboard/catalog.js +322 -0
  48. package/dist/components/dashboard/data-boundary.cjs +29 -0
  49. package/dist/components/dashboard/data-boundary.d.cts +21 -0
  50. package/dist/components/dashboard/data-boundary.d.ts +21 -0
  51. package/dist/components/dashboard/data-boundary.js +29 -0
  52. package/dist/components/dashboard/empty-state.cjs +49 -0
  53. package/dist/components/dashboard/empty-state.d.cts +20 -0
  54. package/dist/components/dashboard/empty-state.d.ts +20 -0
  55. package/dist/components/dashboard/empty-state.js +49 -0
  56. package/dist/components/dashboard/icon-button.cjs +40 -0
  57. package/dist/components/dashboard/icon-button.d.cts +24 -0
  58. package/dist/components/dashboard/icon-button.d.ts +24 -0
  59. package/dist/components/dashboard/icon-button.js +40 -0
  60. package/dist/components/dashboard/index.cjs +15 -0
  61. package/dist/components/dashboard/index.d.cts +24 -0
  62. package/dist/components/dashboard/index.d.ts +24 -0
  63. package/dist/components/dashboard/index.js +15 -0
  64. package/dist/components/dashboard/item-list.cjs +71 -0
  65. package/dist/components/dashboard/item-list.d.cts +28 -0
  66. package/dist/components/dashboard/item-list.d.ts +28 -0
  67. package/dist/components/dashboard/item-list.js +71 -0
  68. package/dist/components/dashboard/key-value.cjs +39 -0
  69. package/dist/components/dashboard/key-value.d.cts +17 -0
  70. package/dist/components/dashboard/key-value.d.ts +17 -0
  71. package/dist/components/dashboard/key-value.js +39 -0
  72. package/dist/components/dashboard/layout.cjs +193 -0
  73. package/dist/components/dashboard/layout.d.cts +91 -0
  74. package/dist/components/dashboard/layout.d.ts +91 -0
  75. package/dist/components/dashboard/layout.js +193 -0
  76. package/dist/components/dashboard/metric.cjs +98 -0
  77. package/dist/components/dashboard/metric.d.cts +44 -0
  78. package/dist/components/dashboard/metric.d.ts +44 -0
  79. package/dist/components/dashboard/metric.js +98 -0
  80. package/dist/components/dashboard/progress-ring.cjs +77 -0
  81. package/dist/components/dashboard/progress-ring.d.cts +25 -0
  82. package/dist/components/dashboard/progress-ring.d.ts +25 -0
  83. package/dist/components/dashboard/progress-ring.js +77 -0
  84. package/dist/components/dashboard/skeletons.cjs +54 -0
  85. package/dist/components/dashboard/skeletons.d.cts +15 -0
  86. package/dist/components/dashboard/skeletons.d.ts +15 -0
  87. package/dist/components/dashboard/skeletons.js +54 -0
  88. package/dist/components/dashboard/spacing.cjs +10 -0
  89. package/dist/components/dashboard/spacing.d.cts +4 -0
  90. package/dist/components/dashboard/spacing.d.ts +4 -0
  91. package/dist/components/dashboard/spacing.js +10 -0
  92. package/dist/components/dashboard/stale-indicator.cjs +42 -0
  93. package/dist/components/dashboard/stale-indicator.d.cts +15 -0
  94. package/dist/components/dashboard/stale-indicator.d.ts +15 -0
  95. package/dist/components/dashboard/stale-indicator.js +42 -0
  96. package/dist/components/dashboard/status.cjs +62 -0
  97. package/dist/components/dashboard/status.d.cts +15 -0
  98. package/dist/components/dashboard/status.d.ts +15 -0
  99. package/dist/components/dashboard/status.js +62 -0
  100. package/dist/components/dashboard/tone.cjs +26 -0
  101. package/dist/components/dashboard/tone.d.cts +9 -0
  102. package/dist/components/dashboard/tone.d.ts +9 -0
  103. package/dist/components/dashboard/tone.js +26 -0
  104. package/dist/components/dashboard/typography.cjs +99 -0
  105. package/dist/components/dashboard/typography.d.cts +57 -0
  106. package/dist/components/dashboard/typography.d.ts +57 -0
  107. package/dist/components/dashboard/typography.js +99 -0
  108. package/dist/components/reference/ActivityExample.cjs +47 -0
  109. package/dist/components/reference/ActivityExample.d.cts +5 -0
  110. package/dist/components/reference/ActivityExample.d.ts +5 -0
  111. package/dist/components/reference/ActivityExample.js +47 -0
  112. package/dist/components/reference/ResourceExample.cjs +54 -0
  113. package/dist/components/reference/ResourceExample.d.cts +5 -0
  114. package/dist/components/reference/ResourceExample.d.ts +5 -0
  115. package/dist/components/reference/ResourceExample.js +54 -0
  116. package/dist/components/reference/SchedulerExample.cjs +68 -0
  117. package/dist/components/reference/SchedulerExample.d.cts +5 -0
  118. package/dist/components/reference/SchedulerExample.d.ts +5 -0
  119. package/dist/components/reference/SchedulerExample.js +68 -0
  120. package/dist/components/reference/StarterExample.cjs +34 -0
  121. package/dist/components/reference/StarterExample.d.cts +5 -0
  122. package/dist/components/reference/StarterExample.d.ts +5 -0
  123. package/dist/components/reference/StarterExample.js +34 -0
  124. package/dist/components/reference/index.cjs +10 -0
  125. package/dist/components/reference/index.d.cts +5 -0
  126. package/dist/components/reference/index.d.ts +5 -0
  127. package/dist/components/reference/index.js +10 -0
  128. package/dist/components/ui/badge.cjs +5 -1
  129. package/dist/components/ui/badge.d.cts +1 -1
  130. package/dist/components/ui/badge.d.ts +1 -1
  131. package/dist/components/ui/badge.js +5 -1
  132. package/dist/dashboard-catalog.cjs +8 -0
  133. package/dist/dashboard-catalog.d.cts +1 -0
  134. package/dist/dashboard-catalog.d.ts +1 -0
  135. package/dist/dashboard-catalog.js +8 -0
  136. package/dist/index.cjs +1 -0
  137. package/dist/index.d.cts +15 -0
  138. package/dist/index.d.ts +15 -0
  139. package/dist/index.js +1 -0
  140. package/dist/reference.cjs +12 -0
  141. package/dist/reference.d.cts +5 -0
  142. package/dist/reference.d.ts +5 -0
  143. package/dist/reference.js +12 -0
  144. package/dist/styles/glass-board.css +43 -0
  145. package/dist/styles/globals.css +59 -0
  146. package/package.json +70 -41
  147. package/src/components/context-editor/CapabilitySection.tsx +85 -0
  148. package/src/components/context-editor/ContextEditor.tsx +138 -0
  149. package/src/components/context-editor/PresetBar.tsx +94 -0
  150. package/src/components/context-editor/SkillsSection.tsx +37 -0
  151. package/src/components/context-editor/SystemPromptSection.tsx +88 -0
  152. package/src/components/context-editor/ToolsSection.tsx +37 -0
  153. package/src/components/context-editor/index.ts +7 -0
  154. package/src/components/context-editor/parts.tsx +217 -0
  155. package/src/components/context-editor/use-context-editor-model.ts +296 -0
  156. package/src/components/dashboard/__tests__/catalog.test.ts +57 -0
  157. package/src/components/dashboard/__tests__/glass.test.tsx +42 -0
  158. package/src/components/dashboard/__tests__/state.test.tsx +110 -0
  159. package/src/components/dashboard/__tests__/variants.test.tsx +95 -0
  160. package/src/components/dashboard/activity-list.tsx +99 -0
  161. package/src/components/dashboard/catalog.ts +351 -0
  162. package/src/components/dashboard/data-boundary.tsx +54 -0
  163. package/src/components/dashboard/empty-state.tsx +70 -0
  164. package/src/components/dashboard/icon-button.tsx +58 -0
  165. package/src/components/dashboard/index.ts +21 -0
  166. package/src/components/dashboard/item-list.tsx +106 -0
  167. package/src/components/dashboard/key-value.tsx +54 -0
  168. package/src/components/dashboard/layout.tsx +286 -0
  169. package/src/components/dashboard/metric.tsx +139 -0
  170. package/src/components/dashboard/progress-ring.tsx +100 -0
  171. package/src/components/dashboard/skeletons.tsx +73 -0
  172. package/src/components/dashboard/spacing.ts +14 -0
  173. package/src/components/dashboard/stale-indicator.tsx +55 -0
  174. package/src/components/dashboard/status.tsx +75 -0
  175. package/src/components/dashboard/tone.ts +35 -0
  176. package/src/components/dashboard/typography.tsx +151 -0
  177. package/src/components/reference/ActivityExample.tsx +68 -0
  178. package/src/components/reference/ResourceExample.tsx +78 -0
  179. package/src/components/reference/SchedulerExample.tsx +88 -0
  180. package/src/components/reference/StarterExample.tsx +48 -0
  181. package/src/components/reference/index.ts +10 -0
  182. package/src/components/ui/badge.tsx +6 -0
  183. package/src/dashboard-catalog.ts +13 -0
  184. package/src/index.ts +5 -0
  185. package/src/reference.ts +15 -0
  186. package/src/styles/glass-board.css +43 -0
  187. package/src/styles/globals.css +59 -0
  188. package/tsup.config.ts +6 -2
  189. package/vitest.config.ts +8 -0
  190. package/LICENSE +0 -201
package/package.json CHANGED
@@ -1,37 +1,67 @@
1
1
  {
2
2
  "name": "@sero-ai/ui",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Shared UI components, AI elements, and design tokens for the Sero platform",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
8
  "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/index.cjs"
13
- },
14
- "./theme": {
15
- "types": "./dist/theme/index.d.ts",
16
- "import": "./dist/theme/index.js",
17
- "require": "./dist/theme/index.cjs"
18
- },
19
- "./theme/*": {
20
- "types": "./dist/theme/*.d.ts",
21
- "import": "./dist/theme/*.js",
22
- "require": "./dist/theme/*.cjs"
23
- },
24
- "./styles/*": "./dist/styles/*",
9
+ ".": "./src/index.ts",
10
+ "./dashboard-catalog": "./src/dashboard-catalog.ts",
11
+ "./reference": "./src/reference.ts",
12
+ "./theme": "./src/theme/index.ts",
13
+ "./theme/*": "./src/theme/*",
14
+ "./styles/*": "./src/styles/*",
25
15
  "./package.json": "./package.json",
26
- "./*": {
27
- "types": "./dist/*.d.ts",
28
- "import": "./dist/*.js",
29
- "require": "./dist/*.cjs"
16
+ "./*": "./src/*"
17
+ },
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "typecheck": "tsc --noEmit",
21
+ "test": "vitest run"
22
+ },
23
+ "publishConfig": {
24
+ "main": "./dist/index.js",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs"
32
+ },
33
+ "./dashboard-catalog": {
34
+ "types": "./dist/dashboard-catalog.d.ts",
35
+ "import": "./dist/dashboard-catalog.js",
36
+ "require": "./dist/dashboard-catalog.cjs"
37
+ },
38
+ "./reference": {
39
+ "types": "./dist/reference.d.ts",
40
+ "import": "./dist/reference.js",
41
+ "require": "./dist/reference.cjs"
42
+ },
43
+ "./theme": {
44
+ "types": "./dist/theme/index.d.ts",
45
+ "import": "./dist/theme/index.js",
46
+ "require": "./dist/theme/index.cjs"
47
+ },
48
+ "./theme/*": {
49
+ "types": "./dist/theme/*.d.ts",
50
+ "import": "./dist/theme/*.js",
51
+ "require": "./dist/theme/*.cjs"
52
+ },
53
+ "./styles/*": "./dist/styles/*",
54
+ "./package.json": "./package.json",
55
+ "./*": {
56
+ "types": "./dist/*.d.ts",
57
+ "import": "./dist/*.js",
58
+ "require": "./dist/*.cjs"
59
+ }
30
60
  }
31
61
  },
32
62
  "peerDependencies": {
33
- "react": "^19.2.4",
34
- "react-dom": "^19.2.4"
63
+ "react": "catalog:peer",
64
+ "react-dom": "catalog:peer"
35
65
  },
36
66
  "dependencies": {
37
67
  "@base-ui/react": "^1.3.0",
@@ -40,6 +70,7 @@
40
70
  "@radix-ui/react-use-controllable-state": "^1.2.2",
41
71
  "@remixicon/react": "^4.9.0",
42
72
  "@rive-app/react-webgl2": "^4.27.1",
73
+ "@sero-ai/common": "workspace:*",
43
74
  "@streamdown/cjk": "^1.0.2",
44
75
  "@streamdown/code": "^1.1.0",
45
76
  "@streamdown/math": "^1.0.2",
@@ -53,9 +84,9 @@
53
84
  "date-fns": "^4.1.0",
54
85
  "embla-carousel-react": "^8.6.0",
55
86
  "input-otp": "^1.4.2",
56
- "lucide-react": "^0.563.0",
87
+ "lucide-react": "catalog:",
57
88
  "media-chrome": "^4.18.0",
58
- "motion": "^12.34.0",
89
+ "motion": "catalog:",
59
90
  "nanoid": "^5.1.11",
60
91
  "next-themes": "^0.4.6",
61
92
  "radix-ui": "^1.4.3",
@@ -67,27 +98,25 @@
67
98
  "recharts": "2.15.4",
68
99
  "shiki": "^3.23.0",
69
100
  "sonner": "^2.0.7",
70
- "streamdown": "^2.5.0",
101
+ "streamdown": "catalog:",
71
102
  "tailwind-merge": "^3.5.0",
72
103
  "tokenlens": "^1.3.1",
73
104
  "use-stick-to-bottom": "^1.1.4",
74
- "vaul": "^1.1.2",
75
- "@sero-ai/common": "0.3.1"
105
+ "vaul": "^1.1.2"
76
106
  },
77
107
  "devDependencies": {
78
108
  "@hookform/resolvers": "^5.2.2",
79
- "@types/react": "^19.2.13",
80
- "@types/react-dom": "^19.2.3",
109
+ "@types/react": "catalog:",
110
+ "@types/react-dom": "catalog:",
81
111
  "@types/react-syntax-highlighter": "^15.5.13",
112
+ "jsdom": "^26.1.0",
113
+ "react": "catalog:peer",
114
+ "react-dom": "catalog:peer",
82
115
  "shadcn": "^3.8.5",
83
- "tailwindcss": "4.1.18",
116
+ "tailwindcss": "catalog:",
84
117
  "tsup": "^8.5.1",
85
118
  "tw-animate-css": "^1.4.0",
86
- "typescript": "^5.9.3"
87
- },
88
- "scripts": {
89
- "build": "tsup",
90
- "typecheck": "tsc --noEmit"
91
- },
92
- "module": "./dist/index.js"
93
- }
119
+ "typescript": "catalog:",
120
+ "vitest": "catalog:"
121
+ }
122
+ }
@@ -0,0 +1,85 @@
1
+ import type { LucideIcon } from 'lucide-react';
2
+ import type { ContextSkillInfo, ContextToolInfo } from '@sero-ai/common';
3
+ import { Switch } from '../ui/switch';
4
+ import { ContextSection, ToggleRow } from './parts';
5
+
6
+ interface CapabilitySectionProps<TItem extends ContextToolInfo | ContextSkillInfo> {
7
+ icon: LucideIcon;
8
+ title: string;
9
+ tint: 'amber' | 'violet';
10
+ includeAllLabel: string;
11
+ emptyLabel: string;
12
+ items: TItem[];
13
+ allDisabled: boolean;
14
+ enabledCount: number;
15
+ isEnabled: (name: string) => boolean;
16
+ onToggle: (name: string) => void;
17
+ onToggleAll: (disabled: boolean) => void;
18
+ }
19
+
20
+ export function CapabilitySection<TItem extends ContextToolInfo | ContextSkillInfo>({
21
+ icon,
22
+ title,
23
+ tint,
24
+ includeAllLabel,
25
+ emptyLabel,
26
+ items,
27
+ allDisabled,
28
+ enabledCount,
29
+ isEnabled,
30
+ onToggle,
31
+ onToggleAll,
32
+ }: CapabilitySectionProps<TItem>) {
33
+ const badgeVariant = allDisabled
34
+ ? 'disabled' as const
35
+ : enabledCount < items.length
36
+ ? 'partial' as const
37
+ : 'default' as const;
38
+
39
+ return (
40
+ <ContextSection
41
+ icon={icon}
42
+ title={title}
43
+ tint={tint}
44
+ count={items.length}
45
+ badge={
46
+ items.length > 0 && enabledCount < items.length
47
+ ? `${enabledCount}/${items.length} included`
48
+ : undefined
49
+ }
50
+ badgeVariant={badgeVariant}
51
+ defaultOpen={false}
52
+ >
53
+ <div className="space-y-1">
54
+ {items.length > 0 && (
55
+ <div className="mb-1 flex items-center justify-between rounded-md border-b border-border/20 px-2 pb-2">
56
+ <span className="text-[11px] font-medium text-[var(--text-secondary)]">
57
+ {includeAllLabel}
58
+ </span>
59
+ <Switch
60
+ size="sm"
61
+ checked={!allDisabled}
62
+ onCheckedChange={(checked) => onToggleAll(!checked)}
63
+ />
64
+ </div>
65
+ )}
66
+
67
+ {items.map((item) => (
68
+ <ToggleRow
69
+ key={item.name}
70
+ name={item.name}
71
+ description={item.description}
72
+ enabled={isEnabled(item.name)}
73
+ onToggle={() => onToggle(item.name)}
74
+ />
75
+ ))}
76
+
77
+ {items.length === 0 && (
78
+ <span className="text-[11px] italic text-[var(--text-muted)]">
79
+ {emptyLabel}
80
+ </span>
81
+ )}
82
+ </div>
83
+ </ContextSection>
84
+ );
85
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * ContextEditor — shared, controlled context editor dialog.
3
+ *
4
+ * Edits a system prompt plus tool/skill enablement and saved presets, against
5
+ * an `available` context. Host-agnostic: the consumer supplies the available
6
+ * context, the current overrides, the preset list, and the persistence
7
+ * callbacks. Used by the desktop chat session editor and by app modules (e.g.
8
+ * the Orchestrator loop context override).
9
+ */
10
+
11
+ import { useState } from 'react';
12
+ import { AlertCircle, Loader2, Settings2 } from 'lucide-react';
13
+ import type { AvailableContext, ContextOverrides, ContextPreset } from '@sero-ai/common';
14
+ import {
15
+ Dialog,
16
+ DialogContent,
17
+ DialogDescription,
18
+ DialogFooter,
19
+ DialogHeader,
20
+ DialogTitle,
21
+ } from '../ui/dialog';
22
+ import { ScrollArea } from '../ui/scroll-area';
23
+ import { PresetBar } from './PresetBar';
24
+ import { SkillsSection } from './SkillsSection';
25
+ import { SystemPromptSection, type SystemPromptCopy } from './SystemPromptSection';
26
+ import { ToolsSection } from './ToolsSection';
27
+ import { useContextEditorModel, type ContextPresetBody } from './use-context-editor-model';
28
+
29
+ export interface ContextEditorProps {
30
+ open: boolean;
31
+ onOpenChange: (open: boolean) => void;
32
+ available: AvailableContext | null;
33
+ initialOverrides: ContextOverrides | null;
34
+ presets: ContextPreset[];
35
+ /** Persist the chosen overrides. Return false to keep the dialog open (error). */
36
+ onApply: (overrides: ContextOverrides | null) => Promise<boolean> | boolean;
37
+ onSavePreset: (name: string, preset: ContextPresetBody) => void;
38
+ onDeletePreset: (id: string) => void;
39
+ applyError?: string | null;
40
+ title?: string;
41
+ description?: string;
42
+ systemPromptCopy?: SystemPromptCopy;
43
+ applyLabel?: string;
44
+ /** Hide the Tools section (e.g. the Orchestrator picks tools per step, not per loop). */
45
+ hideTools?: boolean;
46
+ }
47
+
48
+ export function ContextEditor({
49
+ open,
50
+ onOpenChange,
51
+ available,
52
+ initialOverrides,
53
+ presets,
54
+ onApply,
55
+ onSavePreset,
56
+ onDeletePreset,
57
+ applyError,
58
+ title = 'Context Editor',
59
+ description = 'Configure what is included in the LLM context.',
60
+ systemPromptCopy,
61
+ applyLabel = 'Apply',
62
+ hideTools = false,
63
+ }: ContextEditorProps) {
64
+ const model = useContextEditorModel({ open, available, initialOverrides, presets, onSavePreset, onDeletePreset });
65
+ const [applying, setApplying] = useState(false);
66
+
67
+ const handleApply = async () => {
68
+ setApplying(true);
69
+ try {
70
+ const ok = await onApply(model.buildOverrides());
71
+ if (ok !== false) onOpenChange(false);
72
+ } finally {
73
+ setApplying(false);
74
+ }
75
+ };
76
+
77
+ return (
78
+ <Dialog open={open} onOpenChange={onOpenChange}>
79
+ <DialogContent
80
+ className="max-h-[85vh] overflow-hidden bg-[var(--bg-surface)] sm:max-w-[58rem]"
81
+ showCloseButton
82
+ >
83
+ <DialogHeader>
84
+ <DialogTitle className="flex items-center gap-2 text-sm">
85
+ <Settings2 className="size-4 text-[var(--text-muted)]" />
86
+ {title}
87
+ </DialogTitle>
88
+ <DialogDescription className="text-xs">{description}</DialogDescription>
89
+ </DialogHeader>
90
+
91
+ <PresetBar {...model.preset} />
92
+
93
+ {!available ? (
94
+ <div className="flex items-center justify-center gap-2 py-8">
95
+ <Loader2 className="size-4 animate-spin text-[var(--text-muted)]" />
96
+ <span className="text-xs text-[var(--text-muted)]">Loading context…</span>
97
+ </div>
98
+ ) : (
99
+ <ScrollArea className="max-h-[65vh] overflow-hidden">
100
+ <div className="min-w-0 space-y-2 pr-2">
101
+ <SystemPromptSection
102
+ displayedPrompt={model.displayedPrompt}
103
+ systemPrompt={model.systemPrompt}
104
+ onSystemPromptChange={model.setSystemPrompt}
105
+ copy={systemPromptCopy}
106
+ />
107
+ {!hideTools && <ToolsSection {...model.tools} />}
108
+ <SkillsSection {...model.skills} />
109
+ </div>
110
+ </ScrollArea>
111
+ )}
112
+
113
+ {applyError && (
114
+ <div className="flex items-start gap-2 rounded-md border border-status-error-border bg-status-error-muted px-3 py-2">
115
+ <AlertCircle className="mt-0.5 size-3.5 shrink-0 text-status-error" />
116
+ <span className="text-[11px] text-status-error">{applyError}</span>
117
+ </div>
118
+ )}
119
+
120
+ <DialogFooter>
121
+ <button type="button"
122
+ onClick={() => onOpenChange(false)}
123
+ className="rounded-md border border-border/50 px-3 py-1.5 text-xs text-[var(--text-secondary)] transition-colors hover:bg-[var(--bg-elevated)]"
124
+ >
125
+ Cancel
126
+ </button>
127
+ <button type="button"
128
+ onClick={() => { void handleApply(); }}
129
+ disabled={!available || applying}
130
+ className="rounded-md bg-[var(--accent-primary)] px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-[var(--accent-hover)] disabled:opacity-50"
131
+ >
132
+ {applyLabel}
133
+ </button>
134
+ </DialogFooter>
135
+ </DialogContent>
136
+ </Dialog>
137
+ );
138
+ }
@@ -0,0 +1,94 @@
1
+ import { RotateCcw, Save, Trash2 } from 'lucide-react';
2
+ import type { ContextPreset } from '@sero-ai/common';
3
+ import {
4
+ Select,
5
+ SelectContent,
6
+ SelectItem,
7
+ SelectTrigger,
8
+ SelectValue,
9
+ } from '../ui/select';
10
+ import { SavePresetInput } from './parts';
11
+
12
+ interface PresetBarProps {
13
+ allPresets: ContextPreset[];
14
+ activePresetId: string | null;
15
+ activeUserPreset: ContextPreset | null;
16
+ hasOverrides: boolean;
17
+ showSaveInput: boolean;
18
+ onPresetChange: (id: string) => void;
19
+ onDelete: (id: string) => void;
20
+ onReset: () => void;
21
+ onShowSave: () => void;
22
+ onSave: (name: string) => void;
23
+ onCancelSave: () => void;
24
+ }
25
+
26
+ export function PresetBar({
27
+ allPresets,
28
+ activePresetId,
29
+ activeUserPreset,
30
+ hasOverrides,
31
+ showSaveInput,
32
+ onPresetChange,
33
+ onDelete,
34
+ onReset,
35
+ onShowSave,
36
+ onSave,
37
+ onCancelSave,
38
+ }: PresetBarProps) {
39
+ return (
40
+ <>
41
+ <div className="flex items-center gap-2">
42
+ <span className="text-[11px] font-medium text-[var(--text-muted)]">
43
+ Preset:
44
+ </span>
45
+ <Select value={activePresetId ?? ''} onValueChange={onPresetChange}>
46
+ <SelectTrigger size="sm" className="h-7 w-40 text-xs">
47
+ <SelectValue placeholder="Custom" />
48
+ </SelectTrigger>
49
+ <SelectContent>
50
+ {allPresets.map((preset) => (
51
+ <SelectItem key={preset.id} value={preset.id}>
52
+ {preset.name}
53
+ </SelectItem>
54
+ ))}
55
+ </SelectContent>
56
+ </Select>
57
+
58
+ {activeUserPreset && (
59
+ <button type="button"
60
+ onClick={() => onDelete(activeUserPreset.id)}
61
+ className="flex items-center gap-1 rounded-md px-1.5 py-1 text-[var(--text-muted)] transition-colors hover:bg-status-error-muted hover:text-status-error"
62
+ title={`Delete "${activeUserPreset.name}"`}
63
+ >
64
+ <Trash2 className="size-3" />
65
+ </button>
66
+ )}
67
+
68
+ {!showSaveInput && (
69
+ <button type="button"
70
+ onClick={onShowSave}
71
+ className="flex items-center gap-1 rounded-md px-2 py-1 text-[11px] text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-elevated)] hover:text-[var(--text-secondary)]"
72
+ >
73
+ <Save className="size-3" />
74
+ Save as
75
+ </button>
76
+ )}
77
+
78
+ {hasOverrides && (
79
+ <button type="button"
80
+ onClick={onReset}
81
+ className="ml-auto flex items-center gap-1 rounded-md px-2 py-1 text-[11px] text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-elevated)] hover:text-[var(--text-secondary)]"
82
+ >
83
+ <RotateCcw className="size-3" />
84
+ Reset
85
+ </button>
86
+ )}
87
+ </div>
88
+
89
+ {showSaveInput && (
90
+ <SavePresetInput onSave={onSave} onCancel={onCancelSave} />
91
+ )}
92
+ </>
93
+ );
94
+ }
@@ -0,0 +1,37 @@
1
+ import { Sparkles } from 'lucide-react';
2
+ import type { ContextSkillInfo } from '@sero-ai/common';
3
+ import { CapabilitySection } from './CapabilitySection';
4
+
5
+ interface SkillsSectionProps {
6
+ items: ContextSkillInfo[];
7
+ allDisabled: boolean;
8
+ enabledCount: number;
9
+ isEnabled: (name: string) => boolean;
10
+ onToggle: (name: string) => void;
11
+ onToggleAll: (disabled: boolean) => void;
12
+ }
13
+
14
+ export function SkillsSection({
15
+ items,
16
+ allDisabled,
17
+ enabledCount,
18
+ isEnabled,
19
+ onToggle,
20
+ onToggleAll,
21
+ }: SkillsSectionProps) {
22
+ return (
23
+ <CapabilitySection
24
+ icon={Sparkles}
25
+ title="Skills"
26
+ tint="violet"
27
+ includeAllLabel="Include all skills"
28
+ emptyLabel="No skills available"
29
+ items={items}
30
+ allDisabled={allDisabled}
31
+ enabledCount={enabledCount}
32
+ isEnabled={isEnabled}
33
+ onToggle={onToggle}
34
+ onToggleAll={onToggleAll}
35
+ />
36
+ );
37
+ }
@@ -0,0 +1,88 @@
1
+ import { FileText } from 'lucide-react';
2
+ import { ContextSection } from './parts';
3
+
4
+ export interface SystemPromptCopy {
5
+ /** Section title. Defaults to "System Prompt". */
6
+ title?: string;
7
+ /** Help text when no override is set. */
8
+ defaultHint?: string;
9
+ /** Help text when the prompt is set to empty string. */
10
+ emptyHint?: string;
11
+ /** Help text when a custom prompt is set. */
12
+ customHint?: string;
13
+ /** Textarea placeholder. */
14
+ placeholder?: string;
15
+ /** Footer note under the textarea. */
16
+ footnote?: string;
17
+ }
18
+
19
+ const DEFAULT_COPY: Required<SystemPromptCopy> = {
20
+ title: 'System Prompt',
21
+ defaultHint: 'Using the default system prompt',
22
+ emptyHint: 'System prompt excluded',
23
+ customHint: 'Using a custom system prompt',
24
+ placeholder: 'Enter system prompt...',
25
+ footnote: 'Leave the prompt blank to exclude it entirely, or reset to use the default.',
26
+ };
27
+
28
+ interface SystemPromptSectionProps {
29
+ displayedPrompt: string;
30
+ systemPrompt: string | null;
31
+ onSystemPromptChange: (value: string | null) => void;
32
+ copy?: SystemPromptCopy;
33
+ }
34
+
35
+ export function SystemPromptSection({
36
+ displayedPrompt,
37
+ systemPrompt,
38
+ onSystemPromptChange,
39
+ copy,
40
+ }: SystemPromptSectionProps) {
41
+ const c = { ...DEFAULT_COPY, ...copy };
42
+
43
+ return (
44
+ <ContextSection
45
+ icon={FileText}
46
+ title={c.title}
47
+ tint="blue"
48
+ badge={
49
+ systemPrompt !== null
50
+ ? (systemPrompt === '' ? 'excluded' : 'custom')
51
+ : undefined
52
+ }
53
+ badgeVariant={
54
+ systemPrompt !== null
55
+ ? (systemPrompt === '' ? 'disabled' : 'modified')
56
+ : 'default'
57
+ }
58
+ defaultOpen={false}
59
+ >
60
+ <div className="space-y-2">
61
+ <div className="flex items-center justify-between">
62
+ <span className="text-[10px] text-[var(--text-muted)]">
63
+ {systemPrompt === null
64
+ ? c.defaultHint
65
+ : systemPrompt === ''
66
+ ? c.emptyHint
67
+ : c.customHint}
68
+ </span>
69
+ {systemPrompt !== null && (
70
+ <button type="button"
71
+ onClick={() => onSystemPromptChange(null)}
72
+ className="text-[10px] text-[var(--text-muted)] transition-colors hover:text-[var(--text-secondary)]"
73
+ >
74
+ Reset to default
75
+ </button>
76
+ )}
77
+ </div>
78
+ <textarea aria-label="System prompt"
79
+ value={displayedPrompt}
80
+ onChange={(event) => onSystemPromptChange(event.target.value)}
81
+ className="h-80 w-full resize-y rounded-md border border-border/30 bg-[var(--bg-base)] p-2 font-mono text-[11px] text-[var(--text-secondary)] placeholder:text-[var(--text-muted)] outline-none focus:border-[var(--accent-primary)]"
82
+ placeholder={c.placeholder}
83
+ />
84
+ <p className="text-[10px] text-[var(--text-muted)]">{c.footnote}</p>
85
+ </div>
86
+ </ContextSection>
87
+ );
88
+ }
@@ -0,0 +1,37 @@
1
+ import { Wrench } from 'lucide-react';
2
+ import type { ContextToolInfo } from '@sero-ai/common';
3
+ import { CapabilitySection } from './CapabilitySection';
4
+
5
+ interface ToolsSectionProps {
6
+ items: ContextToolInfo[];
7
+ allDisabled: boolean;
8
+ enabledCount: number;
9
+ isEnabled: (name: string) => boolean;
10
+ onToggle: (name: string) => void;
11
+ onToggleAll: (disabled: boolean) => void;
12
+ }
13
+
14
+ export function ToolsSection({
15
+ items,
16
+ allDisabled,
17
+ enabledCount,
18
+ isEnabled,
19
+ onToggle,
20
+ onToggleAll,
21
+ }: ToolsSectionProps) {
22
+ return (
23
+ <CapabilitySection
24
+ icon={Wrench}
25
+ title="Tools"
26
+ tint="amber"
27
+ includeAllLabel="Include all tools"
28
+ emptyLabel="No tools available"
29
+ items={items}
30
+ allDisabled={allDisabled}
31
+ enabledCount={enabledCount}
32
+ isEnabled={isEnabled}
33
+ onToggle={onToggle}
34
+ onToggleAll={onToggleAll}
35
+ />
36
+ );
37
+ }
@@ -0,0 +1,7 @@
1
+ export { ContextEditor, type ContextEditorProps } from './ContextEditor';
2
+ export {
3
+ useContextEditorModel,
4
+ type ContextEditorModel,
5
+ type UseContextEditorModelParams,
6
+ } from './use-context-editor-model';
7
+ export { type SystemPromptCopy } from './SystemPromptSection';