@grackle-ai/web-components 0.108.3 → 0.110.1
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/.rush/temp/3629cc83b3804ac8bcea27905e83162210fb5dd6.tar.log +240 -0
- package/.rush/temp/{28d8c029b5d0c4a740412c3ca7a7aa456ad48c1b.untar.log → 3629cc83b3804ac8bcea27905e83162210fb5dd6.untar.log} +2 -2
- package/.rush/temp/chunked-rush-logs/web-components._phase_build.chunks.jsonl +18 -0
- package/.rush/temp/chunked-rush-logs/web-components._phase_test.chunks.jsonl +121 -0
- package/.rush/temp/f2b8b611fc00c7b912256986db4cc966d6560387.tar.log +12 -0
- package/.rush/temp/{9f397c070a4229568e12d273f46cf0cda84b9bd2.untar.log → f2b8b611fc00c7b912256986db4cc966d6560387.untar.log} +2 -2
- package/.rush/temp/operation/_phase_build/all.log +5 -5
- package/.rush/temp/operation/_phase_build/log-chunks.jsonl +5 -5
- package/.rush/temp/operation/_phase_build/state.json +1 -1
- package/.rush/temp/operation/_phase_test/all.log +21 -21
- package/.rush/temp/operation/_phase_test/log-chunks.jsonl +21 -21
- package/.rush/temp/operation/_phase_test/state.json +1 -1
- package/README.md +95 -0
- package/dist/index.css +1 -1
- package/dist/index.js +5826 -5732
- package/package.json +2 -2
- package/rush-logs/web-components._phase_build.cache.log +1 -1
- package/rush-logs/web-components._phase_build.log +18 -0
- package/rush-logs/web-components._phase_test.cache.log +1 -1
- package/rush-logs/web-components._phase_test.log +121 -0
- package/src/components/layout/AppNav.module.scss +6 -0
- package/src/components/layout/AppNav.stories.tsx +47 -0
- package/src/components/layout/AppNav.tsx +24 -10
- package/src/components/panels/EnvironmentEditPanel.stories.tsx +87 -1
- package/src/components/panels/EnvironmentEditPanel.tsx +143 -34
- package/src/context/GrackleContextTypes.ts +3 -1
- package/src/hooks/types.ts +21 -0
- package/src/index.ts +2 -2
- package/src/mocks/MockGrackleProvider.tsx +7 -0
- package/temp/build/lint/_eslint-5eVG3S6w.json +810 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useCallback, type JSX } from "react";
|
|
2
2
|
import type { ToastVariant } from "../../context/ToastContext.js";
|
|
3
|
-
import type { Environment, Codespace, GitHubAccountData } from "../../hooks/types.js";
|
|
3
|
+
import type { Environment, Codespace, DockerContainer, GitHubAccountData } from "../../hooks/types.js";
|
|
4
4
|
import { ENVIRONMENTS_URL, environmentUrl, useAppNavigate } from "../../utils/navigation.js";
|
|
5
5
|
import { EditableTextField } from "../editable/EditableTextField.js";
|
|
6
6
|
import styles from "./EnvironmentEditPanel.module.scss";
|
|
@@ -35,6 +35,12 @@ interface Props {
|
|
|
35
35
|
codespaceCreating: boolean;
|
|
36
36
|
/** Callback to create a new codespace. */
|
|
37
37
|
onCreateCodespace: (repo: string, machine?: string) => void;
|
|
38
|
+
/** Callback to list running Docker containers available to attach to. */
|
|
39
|
+
onListDockerContainers: () => void;
|
|
40
|
+
/** Running Docker containers available to attach to (docker attach mode). */
|
|
41
|
+
dockerContainers: DockerContainer[];
|
|
42
|
+
/** Non-fatal error from listing Docker containers (e.g. docker CLI unavailable). */
|
|
43
|
+
dockerContainersError: string;
|
|
38
44
|
/** Display a toast notification. */
|
|
39
45
|
onShowToast?: (message: string, variant?: ToastVariant) => void;
|
|
40
46
|
}
|
|
@@ -202,7 +208,7 @@ function CodespacePicker({ codespaceName, onCodespaceNameChange, envName, onEnvN
|
|
|
202
208
|
* - edit: pre-populated form; uses click-to-edit fields that auto-save via
|
|
203
209
|
* updateEnvironment.
|
|
204
210
|
*/
|
|
205
|
-
export function EnvironmentEditPanel({ mode, environmentId, environments, githubAccounts, onAddEnvironment, onUpdateEnvironment, onListCodespaces, codespaces, codespaceError, codespaceListError, codespaceCreating, onCreateCodespace, onShowToast }: Props): JSX.Element {
|
|
211
|
+
export function EnvironmentEditPanel({ mode, environmentId, environments, githubAccounts, onAddEnvironment, onUpdateEnvironment, onListCodespaces, codespaces, codespaceError, codespaceListError, codespaceCreating, onCreateCodespace, onListDockerContainers, dockerContainers, dockerContainersError, onShowToast }: Props): JSX.Element {
|
|
206
212
|
const navigate = useAppNavigate();
|
|
207
213
|
|
|
208
214
|
const isEdit = mode === "edit";
|
|
@@ -222,6 +228,9 @@ export function EnvironmentEditPanel({ mode, environmentId, environments, github
|
|
|
222
228
|
const [repo, setRepo] = useState("");
|
|
223
229
|
const [codespaceName, setCodespaceName] = useState("");
|
|
224
230
|
const [githubAccountId, setGithubAccountId] = useState("");
|
|
231
|
+
// Docker: "create" a new container vs "attach" to an existing one (issue #1223).
|
|
232
|
+
const [dockerMode, setDockerMode] = useState<"create" | "attach">("create");
|
|
233
|
+
const [attachContainer, setAttachContainer] = useState("");
|
|
225
234
|
|
|
226
235
|
// ─── Edit mode state ───────────────────────────────
|
|
227
236
|
|
|
@@ -257,17 +266,24 @@ export function EnvironmentEditPanel({ mode, environmentId, environments, github
|
|
|
257
266
|
config.identityFile = identityFile.trim();
|
|
258
267
|
}
|
|
259
268
|
} else if (adapterType === "docker") {
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
269
|
+
if (dockerMode === "attach") {
|
|
270
|
+
// Attach mode: target an existing container; image/repo are ignored.
|
|
271
|
+
if (attachContainer.trim()) {
|
|
272
|
+
config.attach = attachContainer.trim();
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
if (image.trim()) {
|
|
276
|
+
config.image = image.trim();
|
|
277
|
+
}
|
|
278
|
+
if (repo.trim()) {
|
|
279
|
+
config.repo = repo.trim();
|
|
280
|
+
}
|
|
265
281
|
}
|
|
266
282
|
} else if (adapterType === "codespace") {
|
|
267
283
|
config.codespaceName = codespaceName.trim();
|
|
268
284
|
}
|
|
269
285
|
return config;
|
|
270
|
-
}, [adapterType, host, port, user, identityFile, image, repo, codespaceName]);
|
|
286
|
+
}, [adapterType, host, port, user, identityFile, image, repo, codespaceName, dockerMode, attachContainer]);
|
|
271
287
|
|
|
272
288
|
const isCreateValid = (): boolean => {
|
|
273
289
|
if (!envName.trim()) {
|
|
@@ -279,6 +295,9 @@ export function EnvironmentEditPanel({ mode, environmentId, environments, github
|
|
|
279
295
|
if (adapterType === "codespace" && !codespaceName.trim()) {
|
|
280
296
|
return false;
|
|
281
297
|
}
|
|
298
|
+
if (adapterType === "docker" && dockerMode === "attach" && !attachContainer.trim()) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
282
301
|
if ((adapterType === "local" || adapterType === "ssh") && !isPortValid(port)) {
|
|
283
302
|
return false;
|
|
284
303
|
}
|
|
@@ -533,7 +552,25 @@ export function EnvironmentEditPanel({ mode, environmentId, environments, github
|
|
|
533
552
|
</>
|
|
534
553
|
)}
|
|
535
554
|
|
|
536
|
-
{existingEnv.adapterType === "docker" && (
|
|
555
|
+
{existingEnv.adapterType === "docker" && config.attach !== undefined && (
|
|
556
|
+
<div className={styles.section}>
|
|
557
|
+
<label className={styles.label}>Attach (container)</label>
|
|
558
|
+
<EditableTextField
|
|
559
|
+
value={String(config.attach ?? "")}
|
|
560
|
+
onSave={(v) => saveConfigField("attach", v)}
|
|
561
|
+
validate={(v) => v.trim() === "" ? "Container name is required" : undefined}
|
|
562
|
+
mode="edit"
|
|
563
|
+
fieldId="attach"
|
|
564
|
+
activeFieldId={activeFieldId}
|
|
565
|
+
onActivate={setActiveFieldId}
|
|
566
|
+
placeholder="container name or ID"
|
|
567
|
+
ariaLabel="Attach container"
|
|
568
|
+
data-testid="env-edit-attach"
|
|
569
|
+
/>
|
|
570
|
+
</div>
|
|
571
|
+
)}
|
|
572
|
+
|
|
573
|
+
{existingEnv.adapterType === "docker" && config.attach === undefined && (
|
|
537
574
|
<>
|
|
538
575
|
<div className={styles.section}>
|
|
539
576
|
<label className={styles.label}>Image</label>
|
|
@@ -792,33 +829,105 @@ export function EnvironmentEditPanel({ mode, environmentId, environments, github
|
|
|
792
829
|
{adapterType === "docker" && (
|
|
793
830
|
<>
|
|
794
831
|
<div className={styles.section}>
|
|
795
|
-
<label className={styles.label} htmlFor="env-
|
|
796
|
-
|
|
832
|
+
<label className={styles.label} htmlFor="env-docker-mode">
|
|
833
|
+
Source
|
|
797
834
|
</label>
|
|
798
|
-
<
|
|
799
|
-
id="env-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
type="text"
|
|
815
|
-
value={repo}
|
|
816
|
-
onChange={(e) => setRepo(e.target.value)}
|
|
817
|
-
placeholder="Repo (optional)..."
|
|
818
|
-
className={styles.fieldInput}
|
|
819
|
-
data-testid="env-create-repo"
|
|
820
|
-
/>
|
|
835
|
+
<select
|
|
836
|
+
id="env-docker-mode"
|
|
837
|
+
value={dockerMode}
|
|
838
|
+
onChange={(e) => {
|
|
839
|
+
const next = e.target.value as "create" | "attach";
|
|
840
|
+
setDockerMode(next);
|
|
841
|
+
if (next === "attach") {
|
|
842
|
+
onListDockerContainers();
|
|
843
|
+
}
|
|
844
|
+
}}
|
|
845
|
+
className={styles.adapterSelect}
|
|
846
|
+
data-testid="env-docker-mode"
|
|
847
|
+
>
|
|
848
|
+
<option value="create">Create new container</option>
|
|
849
|
+
<option value="attach">Attach to existing container</option>
|
|
850
|
+
</select>
|
|
821
851
|
</div>
|
|
852
|
+
|
|
853
|
+
{dockerMode === "create" ? (
|
|
854
|
+
<>
|
|
855
|
+
<div className={styles.section}>
|
|
856
|
+
<label className={styles.label} htmlFor="env-create-image">
|
|
857
|
+
Image
|
|
858
|
+
</label>
|
|
859
|
+
<input
|
|
860
|
+
id="env-create-image"
|
|
861
|
+
type="text"
|
|
862
|
+
value={image}
|
|
863
|
+
onChange={(e) => setImage(e.target.value)}
|
|
864
|
+
placeholder="Image (optional)..."
|
|
865
|
+
className={styles.fieldInput}
|
|
866
|
+
data-testid="env-create-image"
|
|
867
|
+
/>
|
|
868
|
+
</div>
|
|
869
|
+
<div className={styles.section}>
|
|
870
|
+
<label className={styles.label} htmlFor="env-create-repo">
|
|
871
|
+
Repo
|
|
872
|
+
</label>
|
|
873
|
+
<input
|
|
874
|
+
id="env-create-repo"
|
|
875
|
+
type="text"
|
|
876
|
+
value={repo}
|
|
877
|
+
onChange={(e) => setRepo(e.target.value)}
|
|
878
|
+
placeholder="Repo (optional)..."
|
|
879
|
+
className={styles.fieldInput}
|
|
880
|
+
data-testid="env-create-repo"
|
|
881
|
+
/>
|
|
882
|
+
</div>
|
|
883
|
+
</>
|
|
884
|
+
) : (
|
|
885
|
+
<div className={styles.section}>
|
|
886
|
+
<label className={styles.label}>Container</label>
|
|
887
|
+
{!dockerContainersError && dockerContainers.length > 0 && (
|
|
888
|
+
<select
|
|
889
|
+
value={attachContainer}
|
|
890
|
+
onChange={(e) => {
|
|
891
|
+
setAttachContainer(e.target.value);
|
|
892
|
+
if (e.target.value && !envName.trim()) {
|
|
893
|
+
setEnvName(e.target.value);
|
|
894
|
+
}
|
|
895
|
+
}}
|
|
896
|
+
className={styles.adapterSelect}
|
|
897
|
+
data-testid="env-docker-container-select"
|
|
898
|
+
>
|
|
899
|
+
<option value="">Select a container...</option>
|
|
900
|
+
{dockerContainers.map((c) => (
|
|
901
|
+
<option key={c.id} value={c.name}>
|
|
902
|
+
{c.name} ({c.image}) {c.status}
|
|
903
|
+
</option>
|
|
904
|
+
))}
|
|
905
|
+
</select>
|
|
906
|
+
)}
|
|
907
|
+
{/* Manual entry fallback: shown on listing error OR when no running
|
|
908
|
+
containers were found, so the user is never stuck with an empty picker. */}
|
|
909
|
+
{(dockerContainersError || dockerContainers.length === 0) && (
|
|
910
|
+
<>
|
|
911
|
+
{dockerContainersError
|
|
912
|
+
? <span className={styles.errorHint}>{dockerContainersError}</span>
|
|
913
|
+
: <span className={styles.creatingHint}>No running containers found.</span>}
|
|
914
|
+
<input
|
|
915
|
+
type="text"
|
|
916
|
+
value={attachContainer}
|
|
917
|
+
onChange={(e) => {
|
|
918
|
+
setAttachContainer(e.target.value);
|
|
919
|
+
if (e.target.value && !envName.trim()) {
|
|
920
|
+
setEnvName(e.target.value);
|
|
921
|
+
}
|
|
922
|
+
}}
|
|
923
|
+
placeholder="Enter container name/ID..."
|
|
924
|
+
className={styles.fieldInput}
|
|
925
|
+
data-testid="env-docker-container-manual"
|
|
926
|
+
/>
|
|
927
|
+
</>
|
|
928
|
+
)}
|
|
929
|
+
</div>
|
|
930
|
+
)}
|
|
822
931
|
</>
|
|
823
932
|
)}
|
|
824
933
|
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
UsageStats, UseKnowledgeResult,
|
|
11
11
|
UseEnvironmentsResult, UseSessionsResult, UseWorkspacesResult,
|
|
12
12
|
UseTasksResult, UseFindingsResult, UseTokensResult,
|
|
13
|
-
UseCredentialsResult, UseCodespacesResult, UsePersonasResult,
|
|
13
|
+
UseCredentialsResult, UseCodespacesResult, UseDockerContainersResult, UsePersonasResult,
|
|
14
14
|
UsePluginsResult,
|
|
15
15
|
UseSchedulesResult,
|
|
16
16
|
UseStreamsResult,
|
|
@@ -38,6 +38,8 @@ export interface UseGrackleSocketResult {
|
|
|
38
38
|
credentials: Omit<UseCredentialsResult, "handleEvent" | "loadCredentials">;
|
|
39
39
|
/** GitHub Codespace state and actions. */
|
|
40
40
|
codespaces: UseCodespacesResult;
|
|
41
|
+
/** Docker container discovery (attach mode) state and actions. */
|
|
42
|
+
dockerContainers: UseDockerContainersResult;
|
|
41
43
|
/** Persona state and actions. */
|
|
42
44
|
personas: Omit<UsePersonasResult, "handleEvent" | "loadPersonas">;
|
|
43
45
|
/** Schedule state and actions. */
|
package/src/hooks/types.ts
CHANGED
|
@@ -162,6 +162,15 @@ export interface Codespace {
|
|
|
162
162
|
gitStatus: string;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/** A running Docker container an environment can attach to (`docker ps`). */
|
|
166
|
+
export interface DockerContainer {
|
|
167
|
+
id: string;
|
|
168
|
+
name: string;
|
|
169
|
+
image: string;
|
|
170
|
+
state: string;
|
|
171
|
+
status: string;
|
|
172
|
+
}
|
|
173
|
+
|
|
165
174
|
/** An agent or script persona configuration. */
|
|
166
175
|
export interface PersonaData {
|
|
167
176
|
id: string;
|
|
@@ -496,6 +505,18 @@ export interface UseCodespacesResult {
|
|
|
496
505
|
domainHook: DomainHook;
|
|
497
506
|
}
|
|
498
507
|
|
|
508
|
+
/** Values returned by the docker-containers domain hook. */
|
|
509
|
+
export interface UseDockerContainersResult {
|
|
510
|
+
/** Running Docker containers available to attach to. */
|
|
511
|
+
dockerContainers: DockerContainer[];
|
|
512
|
+
/** Error message from the most recent list attempt, or empty string. */
|
|
513
|
+
dockerContainersError: string;
|
|
514
|
+
/** Request the current running-container list from the server. */
|
|
515
|
+
listDockerContainers: () => Promise<void>;
|
|
516
|
+
/** Lifecycle hook for connect/disconnect/event routing. */
|
|
517
|
+
domainHook: DomainHook;
|
|
518
|
+
}
|
|
519
|
+
|
|
499
520
|
/** Values returned by the personas domain hook. */
|
|
500
521
|
export interface UsePersonasResult {
|
|
501
522
|
/** All known personas. */
|
package/src/index.ts
CHANGED
|
@@ -125,13 +125,13 @@ export type { UseGrackleSocketResult, GrackleContextType } from "./context/Grack
|
|
|
125
125
|
export type {
|
|
126
126
|
Environment, Session, UsageStats, SessionEvent,
|
|
127
127
|
Workspace, TaskData, FindingData, TokenInfo,
|
|
128
|
-
CredentialProviderConfig, Codespace, PersonaData,
|
|
128
|
+
CredentialProviderConfig, Codespace, DockerContainer, PersonaData,
|
|
129
129
|
ScheduleData, ScheduleUpdate, UseSchedulesResult,
|
|
130
130
|
ProvisionStatus, GrackleEvent, WsMessage, SendFunction,
|
|
131
131
|
GraphNode, GraphLink, NodeDetail, UseKnowledgeResult,
|
|
132
132
|
UseEnvironmentsResult, UseSessionsResult, UseWorkspacesResult,
|
|
133
133
|
UseTasksResult, UseFindingsResult, UseTokensResult,
|
|
134
|
-
UseCredentialsResult, UseCodespacesResult, UsePersonasResult,
|
|
134
|
+
UseCredentialsResult, UseCodespacesResult, UseDockerContainersResult, UsePersonasResult,
|
|
135
135
|
UsePluginsResult, PluginData,
|
|
136
136
|
StreamData, StreamSubscriberData, UseStreamsResult,
|
|
137
137
|
UseGitHubAccountsResult, GitHubAccountData,
|
|
@@ -1067,6 +1067,13 @@ export function MockGrackleProvider({ children }: MockGrackleProviderProps): JSX
|
|
|
1067
1067
|
domainHook: NOOP_DOMAIN_HOOK,
|
|
1068
1068
|
},
|
|
1069
1069
|
|
|
1070
|
+
dockerContainers: {
|
|
1071
|
+
dockerContainers: [],
|
|
1072
|
+
dockerContainersError: "",
|
|
1073
|
+
listDockerContainers: async () => { },
|
|
1074
|
+
domainHook: NOOP_DOMAIN_HOOK,
|
|
1075
|
+
},
|
|
1076
|
+
|
|
1070
1077
|
personas: {
|
|
1071
1078
|
personas,
|
|
1072
1079
|
personasLoading: false,
|