@grackle-ai/web-components 0.107.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/.rush/temp/3ae72563f781afd72723475938136f113846603e.untar.log +10 -0
  2. package/.rush/temp/bc1d5bf9201ce71abeaeaddd096deb9b0805d703.untar.log +10 -0
  3. package/.rush/temp/operation/_phase_build/all.log +18 -0
  4. package/.rush/temp/operation/_phase_build/log-chunks.jsonl +18 -0
  5. package/.rush/temp/operation/_phase_build/state.json +3 -0
  6. package/.rush/temp/operation/_phase_test/all.log +121 -0
  7. package/.rush/temp/operation/_phase_test/log-chunks.jsonl +121 -0
  8. package/.rush/temp/operation/_phase_test/state.json +3 -0
  9. package/.rush/temp/shrinkwrap-deps.json +938 -0
  10. package/.storybook/main.ts +22 -0
  11. package/.storybook/preview.tsx +30 -0
  12. package/config/rig.json +4 -0
  13. package/config/rush-project.json +12 -0
  14. package/dist/index.css +1 -0
  15. package/dist/index.js +39221 -0
  16. package/eslint.config.cjs +5 -0
  17. package/package.json +83 -0
  18. package/rush-logs/web-components._phase_build.cache.log +4 -0
  19. package/rush-logs/web-components._phase_test.cache.log +4 -0
  20. package/src/components/chat/ChatInput.module.scss +81 -0
  21. package/src/components/chat/ChatInput.stories.tsx +91 -0
  22. package/src/components/chat/ChatInput.tsx +168 -0
  23. package/src/components/chat/index.ts +6 -0
  24. package/src/components/dag/DagView.module.scss +149 -0
  25. package/src/components/dag/DagView.stories.tsx +125 -0
  26. package/src/components/dag/DagView.tsx +109 -0
  27. package/src/components/dag/TaskNode.stories.tsx +133 -0
  28. package/src/components/dag/TaskNode.tsx +40 -0
  29. package/src/components/dag/useDagLayout.ts +139 -0
  30. package/src/components/display/Breadcrumbs.module.scss +71 -0
  31. package/src/components/display/Breadcrumbs.stories.tsx +80 -0
  32. package/src/components/display/Breadcrumbs.tsx +46 -0
  33. package/src/components/display/Button.module.scss +110 -0
  34. package/src/components/display/Button.stories.tsx +88 -0
  35. package/src/components/display/Button.tsx +40 -0
  36. package/src/components/display/ConfirmDialog.module.scss +67 -0
  37. package/src/components/display/ConfirmDialog.stories.tsx +81 -0
  38. package/src/components/display/ConfirmDialog.tsx +88 -0
  39. package/src/components/display/CopyButton.module.scss +41 -0
  40. package/src/components/display/CopyButton.stories.tsx +78 -0
  41. package/src/components/display/CopyButton.tsx +64 -0
  42. package/src/components/display/DemoBanner.module.scss +37 -0
  43. package/src/components/display/DemoBanner.stories.tsx +40 -0
  44. package/src/components/display/DemoBanner.tsx +23 -0
  45. package/src/components/display/EventHoverRow.module.scss +102 -0
  46. package/src/components/display/EventHoverRow.stories.tsx +99 -0
  47. package/src/components/display/EventHoverRow.tsx +154 -0
  48. package/src/components/display/EventRenderer.module.scss +272 -0
  49. package/src/components/display/EventRenderer.stories.tsx +186 -0
  50. package/src/components/display/EventRenderer.tsx +271 -0
  51. package/src/components/display/EventStream.module.scss +93 -0
  52. package/src/components/display/EventStream.stories.tsx +249 -0
  53. package/src/components/display/EventStream.tsx +369 -0
  54. package/src/components/display/FloatingActionBar.module.scss +107 -0
  55. package/src/components/display/FloatingActionBar.stories.tsx +122 -0
  56. package/src/components/display/FloatingActionBar.tsx +119 -0
  57. package/src/components/display/SessionAttemptSelector.module.scss +50 -0
  58. package/src/components/display/SessionAttemptSelector.stories.tsx +78 -0
  59. package/src/components/display/SessionAttemptSelector.tsx +49 -0
  60. package/src/components/display/SessionPicker.module.scss +200 -0
  61. package/src/components/display/SessionPicker.stories.tsx +169 -0
  62. package/src/components/display/SessionPicker.tsx +214 -0
  63. package/src/components/display/Skeleton.module.scss +58 -0
  64. package/src/components/display/Skeleton.stories.tsx +94 -0
  65. package/src/components/display/Skeleton.tsx +127 -0
  66. package/src/components/display/Spinner.module.scss +41 -0
  67. package/src/components/display/Spinner.stories.tsx +66 -0
  68. package/src/components/display/Spinner.tsx +32 -0
  69. package/src/components/display/SplashScreen.module.scss +20 -0
  70. package/src/components/display/SplashScreen.stories.tsx +26 -0
  71. package/src/components/display/SplashScreen.tsx +16 -0
  72. package/src/components/display/SplitButton.module.scss +166 -0
  73. package/src/components/display/SplitButton.stories.tsx +95 -0
  74. package/src/components/display/SplitButton.tsx +128 -0
  75. package/src/components/display/Tooltip.module.scss +84 -0
  76. package/src/components/display/Tooltip.stories.tsx +240 -0
  77. package/src/components/display/Tooltip.tsx +184 -0
  78. package/src/components/display/extractText.test.tsx +48 -0
  79. package/src/components/display/index.ts +20 -0
  80. package/src/components/editable/EditableCheckbox.stories.tsx +54 -0
  81. package/src/components/editable/EditableCheckbox.tsx +39 -0
  82. package/src/components/editable/EditableField.module.scss +135 -0
  83. package/src/components/editable/EditableSelect.tsx +164 -0
  84. package/src/components/editable/EditableTextArea.stories.tsx +50 -0
  85. package/src/components/editable/EditableTextArea.tsx +148 -0
  86. package/src/components/editable/EditableTextField.stories.tsx +62 -0
  87. package/src/components/editable/EditableTextField.tsx +153 -0
  88. package/src/components/editable/EnvironmentSelect.module.scss +17 -0
  89. package/src/components/editable/EnvironmentSelect.stories.tsx +61 -0
  90. package/src/components/editable/EnvironmentSelect.tsx +87 -0
  91. package/src/components/editable/index.ts +13 -0
  92. package/src/components/editable/useEditableField.test.tsx +233 -0
  93. package/src/components/editable/useEditableField.ts +173 -0
  94. package/src/components/index.ts +20 -0
  95. package/src/components/knowledge/KnowledgeDetailPanel.module.scss +162 -0
  96. package/src/components/knowledge/KnowledgeDetailPanel.stories.tsx +208 -0
  97. package/src/components/knowledge/KnowledgeDetailPanel.tsx +122 -0
  98. package/src/components/knowledge/KnowledgeGraph.module.scss +110 -0
  99. package/src/components/knowledge/KnowledgeGraph.stories.tsx +180 -0
  100. package/src/components/knowledge/KnowledgeGraph.tsx +455 -0
  101. package/src/components/knowledge/KnowledgeNav.module.scss +130 -0
  102. package/src/components/knowledge/KnowledgeNav.stories.tsx +108 -0
  103. package/src/components/knowledge/KnowledgeNav.tsx +138 -0
  104. package/src/components/knowledge/index.ts +3 -0
  105. package/src/components/layout/AppNav.module.scss +82 -0
  106. package/src/components/layout/AppNav.stories.tsx +115 -0
  107. package/src/components/layout/AppNav.tsx +133 -0
  108. package/src/components/layout/BottomStatusBar.module.scss +58 -0
  109. package/src/components/layout/BottomStatusBar.stories.tsx +35 -0
  110. package/src/components/layout/BottomStatusBar.tsx +206 -0
  111. package/src/components/layout/Sidebar.module.scss +60 -0
  112. package/src/components/layout/Sidebar.stories.tsx +46 -0
  113. package/src/components/layout/Sidebar.tsx +84 -0
  114. package/src/components/layout/StatusBar.module.scss +108 -0
  115. package/src/components/layout/StatusBar.stories.tsx +119 -0
  116. package/src/components/layout/StatusBar.tsx +70 -0
  117. package/src/components/layout/index.ts +9 -0
  118. package/src/components/lists/EnvironmentNav.module.scss +118 -0
  119. package/src/components/lists/EnvironmentNav.stories.tsx +121 -0
  120. package/src/components/lists/EnvironmentNav.tsx +133 -0
  121. package/src/components/lists/FindingsNav.module.scss +126 -0
  122. package/src/components/lists/FindingsNav.tsx +146 -0
  123. package/src/components/lists/TaskList.module.scss +206 -0
  124. package/src/components/lists/TaskList.stories.tsx +401 -0
  125. package/src/components/lists/TaskList.tsx +509 -0
  126. package/src/components/lists/index.ts +6 -0
  127. package/src/components/lists/listHelpers.tsx +130 -0
  128. package/src/components/notifications/Callout.module.scss +83 -0
  129. package/src/components/notifications/Callout.stories.tsx +81 -0
  130. package/src/components/notifications/Callout.tsx +64 -0
  131. package/src/components/notifications/Toast.module.scss +86 -0
  132. package/src/components/notifications/Toast.stories.tsx +71 -0
  133. package/src/components/notifications/Toast.tsx +51 -0
  134. package/src/components/notifications/ToastContainer.module.scss +23 -0
  135. package/src/components/notifications/ToastContainer.stories.tsx +66 -0
  136. package/src/components/notifications/ToastContainer.tsx +29 -0
  137. package/src/components/notifications/UpdateBanner.stories.tsx +77 -0
  138. package/src/components/notifications/UpdateBanner.test.tsx +64 -0
  139. package/src/components/notifications/UpdateBanner.tsx +44 -0
  140. package/src/components/notifications/index.ts +8 -0
  141. package/src/components/panels/AboutPanel.stories.tsx +70 -0
  142. package/src/components/panels/AboutPanel.tsx +66 -0
  143. package/src/components/panels/AppearancePanel.stories.tsx +45 -0
  144. package/src/components/panels/AppearancePanel.tsx +97 -0
  145. package/src/components/panels/CredentialProvidersPanel.stories.tsx +62 -0
  146. package/src/components/panels/CredentialProvidersPanel.tsx +111 -0
  147. package/src/components/panels/EnvironmentEditPanel.module.scss +170 -0
  148. package/src/components/panels/EnvironmentEditPanel.stories.tsx +206 -0
  149. package/src/components/panels/EnvironmentEditPanel.tsx +785 -0
  150. package/src/components/panels/FindingsPanel.module.scss +94 -0
  151. package/src/components/panels/FindingsPanel.stories.tsx +109 -0
  152. package/src/components/panels/FindingsPanel.tsx +76 -0
  153. package/src/components/panels/KeyboardShortcutsPanel.module.scss +65 -0
  154. package/src/components/panels/KeyboardShortcutsPanel.stories.tsx +40 -0
  155. package/src/components/panels/KeyboardShortcutsPanel.tsx +104 -0
  156. package/src/components/panels/PluginsPanel.tsx +77 -0
  157. package/src/components/panels/SettingsPanel.module.scss +336 -0
  158. package/src/components/panels/TaskActionButtons.module.scss +22 -0
  159. package/src/components/panels/TaskActionButtons.stories.tsx +125 -0
  160. package/src/components/panels/TaskActionButtons.tsx +87 -0
  161. package/src/components/panels/TaskEditPanel.module.scss +202 -0
  162. package/src/components/panels/TaskEditPanel.stories.tsx +75 -0
  163. package/src/components/panels/TaskEditPanel.tsx +328 -0
  164. package/src/components/panels/TaskOverviewPanel.module.scss +236 -0
  165. package/src/components/panels/TaskOverviewPanel.stories.tsx +219 -0
  166. package/src/components/panels/TaskOverviewPanel.tsx +270 -0
  167. package/src/components/panels/TokensPanel.stories.tsx +131 -0
  168. package/src/components/panels/TokensPanel.tsx +143 -0
  169. package/src/components/panels/WorkpadPanel.module.scss +39 -0
  170. package/src/components/panels/WorkpadPanel.stories.tsx +56 -0
  171. package/src/components/panels/WorkpadPanel.tsx +63 -0
  172. package/src/components/panels/index.ts +13 -0
  173. package/src/components/personas/McpToolSelector.module.scss +109 -0
  174. package/src/components/personas/McpToolSelector.stories.tsx +129 -0
  175. package/src/components/personas/McpToolSelector.tsx +180 -0
  176. package/src/components/personas/PersonaManager.module.scss +233 -0
  177. package/src/components/personas/PersonaManager.stories.tsx +139 -0
  178. package/src/components/personas/PersonaManager.tsx +122 -0
  179. package/src/components/schedules/ScheduleManager.module.scss +98 -0
  180. package/src/components/schedules/ScheduleManager.stories.tsx +78 -0
  181. package/src/components/schedules/ScheduleManager.tsx +160 -0
  182. package/src/components/settings/SettingsNav.module.scss +82 -0
  183. package/src/components/settings/SettingsNav.stories.tsx +83 -0
  184. package/src/components/settings/SettingsNav.tsx +104 -0
  185. package/src/components/streams/StreamDetailPanel.module.scss +206 -0
  186. package/src/components/streams/StreamDetailPanel.stories.tsx +132 -0
  187. package/src/components/streams/StreamDetailPanel.tsx +119 -0
  188. package/src/components/streams/StreamList.module.scss +92 -0
  189. package/src/components/streams/StreamList.stories.tsx +99 -0
  190. package/src/components/streams/StreamList.tsx +114 -0
  191. package/src/components/streams/index.ts +10 -0
  192. package/src/components/tools/AgentToolCard.module.scss +118 -0
  193. package/src/components/tools/AgentToolCard.stories.tsx +304 -0
  194. package/src/components/tools/AgentToolCard.tsx +247 -0
  195. package/src/components/tools/FileEditCard.stories.tsx +138 -0
  196. package/src/components/tools/FileEditCard.tsx +160 -0
  197. package/src/components/tools/FileReadCard.stories.tsx +120 -0
  198. package/src/components/tools/FileReadCard.tsx +106 -0
  199. package/src/components/tools/FindingCard.stories.tsx +124 -0
  200. package/src/components/tools/FindingCard.tsx +178 -0
  201. package/src/components/tools/GenericToolCard.stories.tsx +80 -0
  202. package/src/components/tools/GenericToolCard.tsx +111 -0
  203. package/src/components/tools/IpcCard.stories.tsx +129 -0
  204. package/src/components/tools/IpcCard.tsx +178 -0
  205. package/src/components/tools/KnowledgeCard.stories.tsx +112 -0
  206. package/src/components/tools/KnowledgeCard.tsx +165 -0
  207. package/src/components/tools/MetadataCard.stories.tsx +32 -0
  208. package/src/components/tools/MetadataCard.tsx +39 -0
  209. package/src/components/tools/SearchCard.stories.tsx +74 -0
  210. package/src/components/tools/SearchCard.tsx +86 -0
  211. package/src/components/tools/ShellCard.stories.tsx +112 -0
  212. package/src/components/tools/ShellCard.tsx +106 -0
  213. package/src/components/tools/TaskCard.stories.tsx +123 -0
  214. package/src/components/tools/TaskCard.tsx +203 -0
  215. package/src/components/tools/TodoCard.module.scss +131 -0
  216. package/src/components/tools/TodoCard.stories.tsx +202 -0
  217. package/src/components/tools/TodoCard.tsx +200 -0
  218. package/src/components/tools/ToolCard.stories.tsx +177 -0
  219. package/src/components/tools/ToolCard.tsx +60 -0
  220. package/src/components/tools/ToolCardProps.ts +20 -0
  221. package/src/components/tools/ToolSearchCard.stories.tsx +81 -0
  222. package/src/components/tools/ToolSearchCard.tsx +86 -0
  223. package/src/components/tools/WorkpadCard.stories.tsx +106 -0
  224. package/src/components/tools/WorkpadCard.tsx +125 -0
  225. package/src/components/tools/classifyTool.test.ts +44 -0
  226. package/src/components/tools/classifyTool.ts +134 -0
  227. package/src/components/tools/parseDiff.ts +95 -0
  228. package/src/components/tools/parseShellOutput.ts +28 -0
  229. package/src/components/tools/toolCardHelpers.test.ts +53 -0
  230. package/src/components/tools/toolCards.module.scss +234 -0
  231. package/src/components/workspace/WorkspaceBoard.module.scss +238 -0
  232. package/src/components/workspace/WorkspaceBoard.stories.tsx +240 -0
  233. package/src/components/workspace/WorkspaceBoard.tsx +232 -0
  234. package/src/components/workspace/WorkspaceFormFields.module.scss +79 -0
  235. package/src/components/workspace/WorkspaceFormFields.stories.tsx +133 -0
  236. package/src/components/workspace/WorkspaceFormFields.tsx +185 -0
  237. package/src/context/GrackleContext.ts +28 -0
  238. package/src/context/GrackleContextTypes.ts +64 -0
  239. package/src/context/SidebarContext.tsx +53 -0
  240. package/src/context/ThemeContext.tsx +21 -0
  241. package/src/context/ToastContext.tsx +56 -0
  242. package/src/hooks/types.ts +864 -0
  243. package/src/hooks/useEventSelection.test.ts +204 -0
  244. package/src/hooks/useEventSelection.ts +158 -0
  245. package/src/hooks/useSmartScroll.ts +151 -0
  246. package/src/hooks/useTheme.ts +228 -0
  247. package/src/index.ts +210 -0
  248. package/src/mocks/MockGrackleProvider.tsx +1397 -0
  249. package/src/mocks/mockData.ts +1966 -0
  250. package/src/mocks/mockKnowledgeData.ts +294 -0
  251. package/src/scss.d.ts +12 -0
  252. package/src/styles/global.scss +244 -0
  253. package/src/styles/mixins.scss +278 -0
  254. package/src/styles/prism-theme.scss +148 -0
  255. package/src/styles/theme.scss +1102 -0
  256. package/src/test-utils/storybook-decorators.tsx +50 -0
  257. package/src/test-utils/storybook-helpers.ts +262 -0
  258. package/src/themes.ts +142 -0
  259. package/src/utils/boardColumns.ts +141 -0
  260. package/src/utils/breadcrumbs.test.ts +285 -0
  261. package/src/utils/breadcrumbs.ts +222 -0
  262. package/src/utils/dashboard.test.ts +156 -0
  263. package/src/utils/dashboard.ts +195 -0
  264. package/src/utils/eventContent.test.ts +353 -0
  265. package/src/utils/eventContent.ts +209 -0
  266. package/src/utils/findingCategory.ts +33 -0
  267. package/src/utils/format.ts +27 -0
  268. package/src/utils/iconSize.ts +18 -0
  269. package/src/utils/navigation.ts +205 -0
  270. package/src/utils/route-config.test.ts +128 -0
  271. package/src/utils/scrollUtils.test.ts +65 -0
  272. package/src/utils/scrollUtils.ts +49 -0
  273. package/src/utils/sessionEvents.test.ts +302 -0
  274. package/src/utils/sessionEvents.ts +233 -0
  275. package/src/utils/taskStatus.tsx +137 -0
  276. package/src/utils/time.ts +92 -0
  277. package/tsconfig.json +8 -0
  278. package/vite.config.ts +20 -0
  279. package/vitest.config.ts +10 -0
@@ -0,0 +1,170 @@
1
+ @use '../../styles/mixins' as *;
2
+
3
+ // =============================================================================
4
+ // Environment Edit Panel — full-panel create/edit form for environments
5
+ // =============================================================================
6
+
7
+ .container {
8
+ flex: 1;
9
+ display: flex;
10
+ flex-direction: column;
11
+ overflow: hidden;
12
+ }
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Header
16
+ // ---------------------------------------------------------------------------
17
+
18
+ .header {
19
+ @include surface-panel;
20
+ padding: var(--space-sm) var(--space-md);
21
+ border-bottom: 1px solid var(--border-subtle);
22
+ display: flex;
23
+ align-items: center;
24
+ justify-content: space-between;
25
+ gap: var(--space-md);
26
+ flex-shrink: 0;
27
+ }
28
+
29
+ .headerTitle {
30
+ font-size: var(--font-size-sm);
31
+ color: var(--text-secondary);
32
+ font-family: var(--font-mono);
33
+ display: flex;
34
+ align-items: center;
35
+ gap: var(--space-sm);
36
+ }
37
+
38
+ .badge {
39
+ background: var(--accent-green-dim);
40
+ color: var(--accent-green);
41
+ border: 1px solid var(--accent-green);
42
+ border-radius: var(--radius-full);
43
+ padding: 1px var(--space-sm);
44
+ font-size: var(--font-size-xs);
45
+ font-family: var(--font-mono);
46
+ }
47
+
48
+ .headerActions {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: var(--space-sm);
52
+ flex-shrink: 0;
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Form body
57
+ // ---------------------------------------------------------------------------
58
+
59
+ .body {
60
+ flex: 1;
61
+ overflow: auto;
62
+ padding: var(--space-lg);
63
+ width: 100%;
64
+
65
+ @include mobile {
66
+ padding: var(--space-md);
67
+ }
68
+ }
69
+
70
+ .formContent {
71
+ display: flex;
72
+ flex-direction: column;
73
+ gap: var(--space-lg);
74
+ max-width: 680px;
75
+
76
+ @include mobile {
77
+ max-width: 100%;
78
+ }
79
+ }
80
+
81
+ .section {
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: var(--space-sm);
85
+ }
86
+
87
+ .label {
88
+ font-size: 11px;
89
+ color: var(--text-tertiary);
90
+ text-transform: uppercase;
91
+ letter-spacing: 0.05em;
92
+ }
93
+
94
+ .nameInput {
95
+ @include input-field;
96
+ font-size: var(--font-size-lg);
97
+ font-weight: var(--font-weight-bold);
98
+ color: var(--text-primary);
99
+ padding: var(--space-sm) var(--space-md);
100
+ width: 100%;
101
+ }
102
+
103
+ .fieldInput {
104
+ @include input-field;
105
+ font-size: var(--font-size-sm);
106
+ color: var(--text-secondary);
107
+ padding: var(--space-sm) var(--space-md);
108
+ width: 100%;
109
+ max-width: 400px;
110
+ }
111
+
112
+ .adapterSelect {
113
+ @include input-field;
114
+ font-size: var(--font-size-sm);
115
+ color: var(--text-secondary);
116
+ padding: var(--space-xs) var(--space-sm);
117
+ width: 100%;
118
+ max-width: 320px;
119
+ cursor: pointer;
120
+ }
121
+
122
+ .readOnlyValue {
123
+ font-size: var(--font-size-sm);
124
+ color: var(--text-secondary);
125
+ padding: var(--space-xs) 0;
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Codespace picker
130
+ // ---------------------------------------------------------------------------
131
+
132
+ .codespaceSection {
133
+ display: flex;
134
+ flex-direction: column;
135
+ gap: var(--space-sm);
136
+ }
137
+
138
+ .codespaceActions {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: var(--space-sm);
142
+ }
143
+
144
+ .creatingHint {
145
+ font-size: var(--font-size-sm);
146
+ color: var(--accent-blue);
147
+ animation: pulse 2s ease-in-out infinite;
148
+ }
149
+
150
+ .errorHint {
151
+ font-size: var(--font-size-sm);
152
+ color: var(--accent-red);
153
+ }
154
+
155
+ @keyframes pulse {
156
+ 0%, 100% { opacity: 1; }
157
+ 50% { opacity: 0.5; }
158
+ }
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // Buttons
162
+ // ---------------------------------------------------------------------------
163
+
164
+ .btnPrimary {
165
+ @include btn-primary;
166
+ }
167
+
168
+ .btnGhost {
169
+ @include btn-ghost(var(--text-secondary));
170
+ }
@@ -0,0 +1,206 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { expect, fn, userEvent } from "@storybook/test";
3
+ import { EnvironmentEditPanel } from "./EnvironmentEditPanel.js";
4
+ import { buildEnvironment } from "../../test-utils/storybook-helpers.js";
5
+
6
+ const meta: Meta<typeof EnvironmentEditPanel> = {
7
+ title: "App/Panels/EnvironmentEditPanel",
8
+ component: EnvironmentEditPanel,
9
+ args: {
10
+ mode: "new",
11
+ environments: [],
12
+ onAddEnvironment: fn(),
13
+ onUpdateEnvironment: fn(),
14
+ onListCodespaces: fn(),
15
+ codespaces: [],
16
+ codespaceError: "",
17
+ codespaceListError: "",
18
+ codespaceCreating: false,
19
+ onCreateCodespace: fn(),
20
+ onShowToast: fn(),
21
+ },
22
+ };
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof EnvironmentEditPanel>;
26
+
27
+ /** The create form renders with default local adapter and an empty name input. */
28
+ export const DefaultCreateForm: Story = {
29
+ play: async ({ canvas }) => {
30
+ // Panel should be visible with form elements
31
+ await expect(canvas.getByTestId("env-create-panel")).toBeInTheDocument();
32
+ await expect(canvas.getByTestId("env-create-name")).toBeInTheDocument();
33
+ await expect(canvas.getByTestId("env-create-submit")).toBeInTheDocument();
34
+ },
35
+ };
36
+
37
+ /** Adapter type dropdown defaults to "local". */
38
+ export const AdapterDefaultsToLocal: Story = {
39
+ play: async ({ canvas }) => {
40
+ const adapterSelect = canvas.getByTestId("env-create-adapter") as HTMLSelectElement;
41
+ await expect(adapterSelect.value).toBe("local");
42
+ },
43
+ };
44
+
45
+ /** Create button is disabled when the name input is empty. */
46
+ export const CreateDisabledWhenEmpty: Story = {
47
+ play: async ({ canvas }) => {
48
+ const createButton = canvas.getByTestId("env-create-submit");
49
+ await expect(createButton).toBeDisabled();
50
+ },
51
+ };
52
+
53
+ /** Create button is enabled when a name is provided for the local adapter. */
54
+ export const CreateEnabledWhenNameFilled: Story = {
55
+ play: async ({ canvas }) => {
56
+ const nameInput = canvas.getByTestId("env-create-name");
57
+ await userEvent.type(nameInput, "my-local");
58
+
59
+ const createButton = canvas.getByTestId("env-create-submit");
60
+ await expect(createButton).toBeEnabled();
61
+ },
62
+ };
63
+
64
+ /** SSH adapter requires a host field — Create stays disabled without it. */
65
+ export const SshRequiresHost: Story = {
66
+ play: async ({ canvas }) => {
67
+ // Select SSH adapter
68
+ const adapterSelect = canvas.getByTestId("env-create-adapter");
69
+ await userEvent.selectOptions(adapterSelect, "ssh");
70
+
71
+ // Fill name but leave host empty
72
+ const nameInput = canvas.getByTestId("env-create-name");
73
+ await userEvent.type(nameInput, "my-ssh");
74
+
75
+ const createButton = canvas.getByTestId("env-create-submit");
76
+ await expect(createButton).toBeDisabled();
77
+
78
+ // Fill host — now it should be enabled
79
+ const hostInput = canvas.getByTestId("env-create-host");
80
+ await userEvent.type(hostInput, "192.168.1.10");
81
+ await expect(createButton).toBeEnabled();
82
+ },
83
+ };
84
+
85
+ /** Create button is disabled when port value is out of the valid range (1-65535). */
86
+ export const PortValidation: Story = {
87
+ play: async ({ canvas }) => {
88
+ const nameInput = canvas.getByTestId("env-create-name");
89
+ await userEvent.type(nameInput, "port-test");
90
+
91
+ const portInput = canvas.getByTestId("env-create-port");
92
+ const createButton = canvas.getByTestId("env-create-submit");
93
+
94
+ // Out-of-range low value
95
+ await userEvent.type(portInput, "0");
96
+ await expect(createButton).toBeDisabled();
97
+
98
+ // Clear and try out-of-range high value
99
+ await userEvent.clear(portInput);
100
+ await userEvent.type(portInput, "99999");
101
+ await expect(createButton).toBeDisabled();
102
+
103
+ // Valid boundary: 1
104
+ await userEvent.clear(portInput);
105
+ await userEvent.type(portInput, "1");
106
+ await expect(createButton).toBeEnabled();
107
+
108
+ // Valid boundary: 65535
109
+ await userEvent.clear(portInput);
110
+ await userEvent.type(portInput, "65535");
111
+ await expect(createButton).toBeEnabled();
112
+
113
+ // Clearing port (optional) keeps button enabled
114
+ await userEvent.clear(portInput);
115
+ await expect(createButton).toBeEnabled();
116
+ },
117
+ };
118
+
119
+ /** Switching adapter type shows the correct conditional fields. */
120
+ export const SwitchingAdapterShowsFields: Story = {
121
+ play: async ({ canvas }) => {
122
+ const adapterSelect = canvas.getByTestId("env-create-adapter");
123
+
124
+ // Local shows host and port
125
+ await expect(canvas.getByTestId("env-create-host")).toBeInTheDocument();
126
+ await expect(canvas.getByTestId("env-create-port")).toBeInTheDocument();
127
+
128
+ // Switch to SSH — shows host, user, port, identity file
129
+ await userEvent.selectOptions(adapterSelect, "ssh");
130
+ await expect(canvas.getByTestId("env-create-host")).toBeInTheDocument();
131
+ await expect(canvas.getByTestId("env-create-user")).toBeInTheDocument();
132
+ await expect(canvas.getByTestId("env-create-port")).toBeInTheDocument();
133
+ await expect(canvas.getByTestId("env-create-identity")).toBeInTheDocument();
134
+
135
+ // Switch to Docker — shows image and repo
136
+ await userEvent.selectOptions(adapterSelect, "docker");
137
+ await expect(canvas.getByTestId("env-create-image")).toBeInTheDocument();
138
+ await expect(canvas.getByTestId("env-create-repo")).toBeInTheDocument();
139
+ },
140
+ };
141
+
142
+ /** When codespace listing fails, a manual entry input appears and the select dropdown is hidden. */
143
+ export const CodespaceManualEntry: Story = {
144
+ args: {
145
+ codespaceListError: "Could not find the `gh` CLI.",
146
+ },
147
+ play: async ({ canvas }) => {
148
+ // Select codespace adapter
149
+ const adapterSelect = canvas.getByTestId("env-create-adapter");
150
+ await userEvent.selectOptions(adapterSelect, "codespace");
151
+
152
+ // Manual input fallback should appear
153
+ await expect(canvas.getByTestId("env-codespace-manual")).toBeInTheDocument();
154
+
155
+ // Error message should be visible
156
+ await expect(canvas.getByText(/gh/)).toBeInTheDocument();
157
+
158
+ // Select dropdown should NOT be in the document when list error is present
159
+ const selectEl = canvas.queryByTestId("env-codespace-select");
160
+ await expect(selectEl).not.toBeInTheDocument();
161
+ },
162
+ };
163
+
164
+ /** Manual codespace entry enables the Create button when name and codespace are filled. */
165
+ export const CodespaceManualEntryEnablesCreate: Story = {
166
+ args: {
167
+ codespaceListError: "Could not find the `gh` CLI.",
168
+ },
169
+ play: async ({ canvas }) => {
170
+ // Select codespace adapter
171
+ const adapterSelect = canvas.getByTestId("env-create-adapter");
172
+ await userEvent.selectOptions(adapterSelect, "codespace");
173
+
174
+ // Fill environment name
175
+ const nameInput = canvas.getByTestId("env-create-name");
176
+ await userEvent.type(nameInput, "my-cs");
177
+
178
+ // Fill manual codespace name
179
+ const manualInput = canvas.getByTestId("env-codespace-manual");
180
+ await userEvent.type(manualInput, "my-codespace-name");
181
+
182
+ // Create button should be enabled
183
+ const createButton = canvas.getByTestId("env-create-submit");
184
+ await expect(createButton).toBeEnabled();
185
+ },
186
+ };
187
+
188
+ /** Edit mode renders pre-populated fields for an existing environment. */
189
+ export const EditModeLocal: Story = {
190
+ args: {
191
+ mode: "edit",
192
+ environmentId: "env-local-01",
193
+ environments: [
194
+ buildEnvironment({
195
+ id: "env-local-01",
196
+ displayName: "My Local Env",
197
+ adapterType: "local",
198
+ adapterConfig: '{"host":"127.0.0.1","port":7434}',
199
+ }),
200
+ ],
201
+ },
202
+ play: async ({ canvas }) => {
203
+ await expect(canvas.getByTestId("env-edit-panel")).toBeInTheDocument();
204
+ await expect(canvas.getByText("local")).toBeInTheDocument();
205
+ },
206
+ };