@exagone313/dsh-podman 0.2.0-rc.3 → 0.2.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.
- package/README.md +14 -2
- package/README.zh.md +14 -2
- package/cordis.patch.yml +58 -0
- package/dist/approval-reasons.d.ts +3 -4
- package/dist/approval-reasons.js +40 -49
- package/dist/approval-reasons.test.js +43 -15
- package/dist/approval.js +27 -10
- package/dist/approval.test.js +165 -34
- package/dist/card-route.d.ts +3 -2
- package/dist/card-route.js +154 -42
- package/dist/card-route.test.js +85 -43
- package/dist/card-test-support.js +39 -66
- package/dist/client/ContainerCard.d.ts +1 -1
- package/dist/client/ContainerCard.js +92 -50
- package/dist/client/card-protocol.d.ts +6 -3
- package/dist/client/container-card-caches.js +11 -6
- package/dist/client/container-card-controller.d.ts +16 -18
- package/dist/client/container-card-controller.js +92 -40
- package/dist/client/container-card-create-modal.d.ts +2 -2
- package/dist/client/container-card-create-modal.js +13 -5
- package/dist/client/container-card-default-env.d.ts +10 -0
- package/dist/client/container-card-default-env.js +69 -0
- package/dist/client/container-card-directory.d.ts +2 -2
- package/dist/client/container-card-directory.js +12 -4
- package/dist/client/container-card-editors.d.ts +3 -3
- package/dist/client/container-card-editors.js +104 -32
- package/dist/client/container-card-images.d.ts +4 -4
- package/dist/client/container-card-images.js +19 -6
- package/dist/client/container-card-paths.d.ts +3 -3
- package/dist/client/container-card-paths.js +18 -11
- package/dist/client/container-card-row.d.ts +10 -6
- package/dist/client/container-card-row.js +82 -13
- package/dist/client/container-card-secrets.d.ts +3 -3
- package/dist/client/container-card-secrets.js +8 -6
- package/dist/client/container-card-shared.d.ts +4 -17
- package/dist/client/container-card-shared.js +9 -25
- package/dist/client/container-card-styles.d.ts +2 -8
- package/dist/client/container-card-styles.js +14 -52
- package/dist/client/container-card-volumes.d.ts +2 -2
- package/dist/client/container-card-volumes.js +9 -5
- package/dist/client/container-card-workspace.d.ts +8 -6
- package/dist/client/container-card-workspace.js +9 -3
- package/dist/client/directory-picker.js +2 -1
- package/dist/client/index.js +13141 -2036
- package/dist/client/locales.d.ts +6 -1
- package/dist/client/locales.js +106 -38
- package/dist/client/podman-terminal-guide.d.ts +11 -0
- package/dist/client/podman-terminal-guide.js +208 -0
- package/dist/client/podman-terminal-title.d.ts +7 -0
- package/dist/client/podman-terminal-title.js +30 -0
- package/dist/client/podman-terminal.d.ts +26 -0
- package/dist/client/podman-terminal.js +412 -0
- package/dist/client/read-only-approval.d.ts +2 -2
- package/dist/client/read-only-approval.js +13 -3
- package/dist/client/slot-contract.d.ts +0 -11
- package/dist/client/terminal-preference.d.ts +26 -0
- package/dist/client/terminal-preference.js +67 -0
- package/dist/client/terminal-protocol.d.ts +85 -0
- package/dist/client/terminal-protocol.js +14 -0
- package/dist/client/terminal-tab.d.ts +60 -0
- package/dist/client/terminal-tab.js +35 -0
- package/dist/client/terminal-targets.d.ts +33 -0
- package/dist/client/terminal-targets.js +65 -0
- package/dist/client/terminal-titles.d.ts +23 -0
- package/dist/client/terminal-titles.js +52 -0
- package/dist/client/terminal-transport.d.ts +63 -0
- package/dist/client/terminal-transport.js +207 -0
- package/dist/client/tool-views.js +215 -49
- package/dist/container-env.d.ts +11 -0
- package/dist/container-env.js +96 -0
- package/dist/container-env.test.d.ts +1 -0
- package/dist/container-env.test.js +93 -0
- package/dist/containers.test.js +196 -18
- package/dist/daemons.test.js +18 -4
- package/dist/dsh-version.d.ts +1 -0
- package/dist/dsh-version.js +46 -0
- package/dist/env-rows.d.ts +14 -0
- package/dist/env-rows.js +52 -0
- package/dist/env-rows.test.d.ts +1 -0
- package/dist/env-rows.test.js +43 -0
- package/dist/fs-provider.d.ts +1 -0
- package/dist/fs-provider.js +26 -12
- package/dist/fs-provider.test.js +51 -10
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +2 -2
- package/dist/grpc/proto/dshctl/v1/control.proto +11 -1
- package/dist/grpc/proto/dshguest/v1/guest.proto +5 -0
- package/dist/grpc/runtime-client.d.ts +3 -1
- package/dist/grpc/runtime-client.js +90 -17
- package/dist/guest-rpc.d.ts +15 -19
- package/dist/guest-rpc.js +289 -70
- package/dist/guest-rpc.test.d.ts +1 -0
- package/dist/guest-rpc.test.js +241 -0
- package/dist/guest-terminal.d.ts +30 -0
- package/dist/guest-terminal.js +196 -0
- package/dist/images.test.js +10 -3
- package/dist/index.d.ts +11 -16
- package/dist/index.js +42 -36
- package/dist/locales.test.d.ts +1 -0
- package/dist/locales.test.js +34 -0
- package/dist/misc.test.js +38 -10
- package/dist/mount-enums.js +2 -1
- package/dist/mount-input.d.ts +2 -0
- package/dist/mount-input.js +38 -22
- package/dist/mounts.test.js +81 -11
- package/dist/naming.test.d.ts +1 -0
- package/dist/naming.test.js +28 -0
- package/dist/output-reader.js +36 -4
- package/dist/package-deps.test.d.ts +1 -0
- package/dist/package-deps.test.js +49 -0
- package/dist/paths.test.js +4 -2
- package/dist/plugin-meta.test.d.ts +1 -0
- package/dist/plugin-meta.test.js +29 -0
- package/dist/project-path.js +33 -7
- package/dist/project-path.test.js +14 -0
- package/dist/prompts.d.ts +0 -4
- package/dist/prompts.js +15 -84
- package/dist/read-only-shell.js +3 -1
- package/dist/read-only-shell.test.js +80 -25
- package/dist/runtime-client.test.d.ts +1 -0
- package/dist/runtime-client.test.js +53 -0
- package/dist/secrets.test.js +21 -6
- package/dist/settings-commands.test.js +61 -14
- package/dist/settings-create.test.js +32 -10
- package/dist/settings-default-env.test.d.ts +1 -0
- package/dist/settings-default-env.test.js +175 -0
- package/dist/settings-mounts.test.js +46 -7
- package/dist/settings-schema.d.ts +6 -11
- package/dist/settings-schema.js +4 -8
- package/dist/settings-schema.test.d.ts +1 -0
- package/dist/settings-schema.test.js +35 -0
- package/dist/settings-workspaces.test.js +38 -63
- package/dist/spill-store.d.ts +1 -0
- package/dist/spill-store.js +17 -4
- package/dist/spill-store.test.js +31 -8
- package/dist/subprocess.d.ts +4 -0
- package/dist/subprocess.js +148 -162
- package/dist/subprocess.test.js +118 -8
- package/dist/terminal-preference.test.d.ts +1 -0
- package/dist/terminal-preference.test.js +62 -0
- package/dist/terminal-route.d.ts +10 -0
- package/dist/terminal-route.js +270 -0
- package/dist/terminal-route.test.d.ts +1 -0
- package/dist/terminal-route.test.js +265 -0
- package/dist/terminal-sessions.d.ts +69 -0
- package/dist/terminal-sessions.js +252 -0
- package/dist/terminal-shells.d.ts +22 -0
- package/dist/terminal-shells.js +99 -0
- package/dist/terminal-tab.test.d.ts +1 -0
- package/dist/terminal-tab.test.js +58 -0
- package/dist/terminal-targets.test.d.ts +1 -0
- package/dist/terminal-targets.test.js +43 -0
- package/dist/terminal-titles.test.d.ts +1 -0
- package/dist/terminal-titles.test.js +36 -0
- package/dist/test-support.d.ts +35 -5
- package/dist/test-support.js +91 -29
- package/dist/tool-defs.js +19 -4
- package/dist/tool-handlers.js +80 -28
- package/dist/tool-params.js +19 -5
- package/dist/version-compat.d.ts +4 -0
- package/dist/version-compat.js +29 -0
- package/dist/version-compat.test.d.ts +1 -0
- package/dist/version-compat.test.js +27 -0
- package/dist/volumes.test.js +1 -1
- package/dist/workspace-binding.d.ts +19 -10
- package/dist/workspace-binding.js +252 -78
- package/dist/workspace-binding.test.js +202 -27
- package/docs/architecture.md +71 -20
- package/docs/architecture.zh.md +62 -12
- package/docs/configuration.md +61 -20
- package/docs/configuration.zh.md +53 -19
- package/docs/development-prompt.md +72 -0
- package/docs/development-prompt.zh.md +69 -0
- package/docs/development.md +184 -29
- package/docs/development.zh.md +165 -26
- package/docs/install-dsh-and-dsh-podman.md +24 -21
- package/docs/install-dsh-and-dsh-podman.zh.md +20 -21
- package/docs/uninstall.md +83 -0
- package/docs/uninstall.zh.md +79 -0
- package/docs/update.md +120 -0
- package/docs/update.zh.md +114 -0
- package/docs/usage.md +108 -61
- package/docs/usage.zh.md +76 -38
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +58 -25
- package/quadlet/dsh-podman-orchestrator.container +46 -0
- package/quadlet/dsh.container +35 -0
- package/LICENSE.pkg +0 -17103
- package/dist/preferences.d.ts +0 -6
- package/dist/preferences.js +0 -27
|
@@ -2,5 +2,5 @@ import { type ContainerCardFace } from "./container-card-controller.js";
|
|
|
2
2
|
import { NS } from "./locales.js";
|
|
3
3
|
import { type InjectFace, type PropsLocale, type PropsRuntime } from "@deepseek-ai/dsh-client-ui-slots";
|
|
4
4
|
import { type ReactNode } from "react";
|
|
5
|
-
export type ContainerCardProps = PropsRuntime<"
|
|
5
|
+
export type ContainerCardProps = PropsRuntime<"plugins.bundle.config"> & PropsLocale<typeof NS> & InjectFace<ContainerCardFace>;
|
|
6
6
|
export declare function ContainerCard(props: ContainerCardProps): ReactNode;
|
|
@@ -2,34 +2,63 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
3
3
|
//
|
|
4
4
|
// SPDX-License-Identifier: MIT
|
|
5
|
-
import { BaseImageRow, ImageBuildModal, ImageItem } from "./container-card-images.js";
|
|
5
|
+
import { BaseImageRow, ImageBuildModal, ImageItem, } from "./container-card-images.js";
|
|
6
6
|
import { CachesSection } from "./container-card-caches.js";
|
|
7
|
+
import { DefaultEnvironmentSection } from "./container-card-default-env.js";
|
|
7
8
|
import { SecretsSection } from "./container-card-secrets.js";
|
|
8
|
-
import {
|
|
9
|
-
import { banner,
|
|
9
|
+
import { ConfirmButton } from "./container-card-shared.js";
|
|
10
|
+
import { banner, footerRow, hint, imageSelect, sectionTitle, } from "./container-card-styles.js";
|
|
10
11
|
import { VolumesSection } from "./container-card-volumes.js";
|
|
11
12
|
import { WorkspaceSection } from "./container-card-workspace.js";
|
|
12
|
-
import { Button,
|
|
13
|
-
import { useEffect,
|
|
13
|
+
import { Button, Modal, writeClipboard, } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
14
|
+
import { useEffect, useRef, useState } from "react";
|
|
15
|
+
// The labels of the version lines, emphasised against their values.
|
|
16
|
+
const versionLabel = { fontWeight: 600 };
|
|
14
17
|
export function ContainerCard(props) {
|
|
15
18
|
const { t } = props;
|
|
16
19
|
const state = props.useContainerCard((snapshot) => snapshot);
|
|
17
|
-
const [open, setOpen] = useState(false);
|
|
18
|
-
const [hovered, setHovered] = useState(false);
|
|
19
20
|
const [buildOpen, setBuildOpen] = useState(false);
|
|
20
21
|
const [imageId, setImageId] = useState("");
|
|
21
22
|
const [parent, setParent] = useState("");
|
|
22
23
|
const [packages, setPackages] = useState([]);
|
|
23
24
|
const [defaultOpen, setDefaultOpen] = useState(false);
|
|
24
25
|
const [defaultImage, setDefaultImage] = useState(state.defaultImage);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
const [copyState, setCopyState] = useState("idle");
|
|
27
|
+
const copyResetTimer = useRef();
|
|
28
|
+
// Copies the version lines as displayed, so a user can paste them into a bug
|
|
29
|
+
// report without transcribing anything.
|
|
30
|
+
const copyVersions = async () => {
|
|
31
|
+
const lines = [
|
|
32
|
+
`${t("dsh")} ${state.dshVersion || t("versionUnknown")}`,
|
|
33
|
+
`${t("cardTitle")} ${state.version}${state.commit ? ` · ${state.commit}` : ""}`,
|
|
34
|
+
`${t("orchestrator")} ${state.orchestratorVersion || t("versionUnknown")}`,
|
|
35
|
+
];
|
|
36
|
+
setCopyState((await writeClipboard(lines.join("\n"))) ? "copied" : "failed");
|
|
37
|
+
if (copyResetTimer.current !== undefined) {
|
|
38
|
+
clearTimeout(copyResetTimer.current);
|
|
39
|
+
}
|
|
40
|
+
copyResetTimer.current = setTimeout(() => setCopyState("idle"), 1500);
|
|
41
|
+
};
|
|
42
|
+
// The reset timer is cancelled on unmount, so it cannot fire against an
|
|
43
|
+
// unmounted component.
|
|
44
|
+
useEffect(() => () => {
|
|
45
|
+
if (copyResetTimer.current !== undefined) {
|
|
46
|
+
clearTimeout(copyResetTimer.current);
|
|
47
|
+
}
|
|
32
48
|
}, []);
|
|
49
|
+
// The Plugins page renders a one-liner in its list and this component again
|
|
50
|
+
// as the entry's page, so the orchestrator snapshot is only fetched for the
|
|
51
|
+
// page. Re-reading on every page view keeps a workspace created since the
|
|
52
|
+
// last visit visible without pressing Reload.
|
|
53
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: refresh per page view
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (props.view === "page")
|
|
56
|
+
props.reload();
|
|
57
|
+
}, [props.view]);
|
|
58
|
+
// The list shows only the one-liner; the page heading already carries the
|
|
59
|
+
// plugin's title and description.
|
|
60
|
+
if (props.view === "summary")
|
|
61
|
+
return t("cardDescription");
|
|
33
62
|
const baseImages = state.images.filter((image) => image.isBase);
|
|
34
63
|
const customImages = state.images.filter((image) => !image.isBase);
|
|
35
64
|
const defaultCandidates = [
|
|
@@ -37,43 +66,56 @@ export function ContainerCard(props) {
|
|
|
37
66
|
...customImages.map((image) => image.imageId),
|
|
38
67
|
];
|
|
39
68
|
if (!state.available) {
|
|
40
|
-
return (
|
|
69
|
+
return (_jsxs("div", { style: { padding: "8px 0", fontSize: "13px", opacity: 0.8 }, children: [_jsx("p", { style: { margin: 0 }, children: t("unavailable") }), state.notice === ""
|
|
70
|
+
? null
|
|
71
|
+
: (_jsxs("p", { style: { margin: "4px 0 0" }, role: "status", children: [t("notice"), ": ", state.notice] })), _jsx("div", { style: { marginTop: "8px" }, children: _jsx(Button, { variant: "outline", size: "sm", disabled: state.busy, onClick: props.reload, children: state.busy ? t("busy") : t("reload") }) })] }));
|
|
41
72
|
}
|
|
42
|
-
return (_jsxs("
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
setImageId("");
|
|
56
|
-
setParent(baseImages[0]?.imageId ?? customImages[0]?.imageId ?? "");
|
|
57
|
-
setPackages([]);
|
|
58
|
-
setBuildOpen(true);
|
|
59
|
-
}, children: t("buildImage") }), _jsx(ConfirmButton, { t: t, label: t("rebuildAllImages"), title: t("confirmTitle"), description: t("confirmRebuildAllImages"), disabled: state.busy, onConfirm: props.rebuildAllImages })] }), _jsx("div", { style: sectionTitle, children: t("baseImagesTitle") }), baseImages.length === 0 ? (_jsx("p", { style: hint, children: t("none") })) : (baseImages.map((image) => (_jsx(BaseImageRow, { t: t, image: image, busy: state.busy, defaultImage: state.defaultImage, onRebuild: props.rebuildBaseImage, onPull: props.pullBaseImage, onSetDefault: props.setDefaultImage }, image.imageId)))), _jsx("div", { style: sectionTitle, children: t("customImagesTitle") }), customImages.length === 0 ? (_jsx("p", { style: hint, children: t("none") })) : (customImages.map((image) => (_jsx(ImageItem, { t: t, image: image, busy: state.busy, defaultImage: state.defaultImage, onRemove: props.removeImage, onRebuild: props.rebuildImage, onSetDefault: props.setDefaultImage }, image.imageId)))), _jsx(ImageBuildModal, { t: t, images: state.images, open: buildOpen, imageId: imageId, parent: parent, packages: packages, onImageId: setImageId, onParent: setParent, onPackages: setPackages, onClose: () => setBuildOpen(false), onBuild: () => {
|
|
60
|
-
props.buildImage(imageId.trim(), parent.trim(), packages);
|
|
73
|
+
return (_jsxs("div", { children: [state.notice === "" ? null : (_jsxs("div", { style: banner, role: "status", children: [t("notice"), ": ", state.notice] })), state.versionState === "ok" ? null : (_jsx("div", { style: state.versionState === "major-mismatch"
|
|
74
|
+
? { ...banner, borderColor: "var(--dsw-alias-state-error-primary)" }
|
|
75
|
+
: banner, role: state.versionState === "major-mismatch" ? "alert" : "status", children: t(state.versionState === "major-mismatch"
|
|
76
|
+
? "versionMismatchMajor"
|
|
77
|
+
: "versionMismatchMinor") })), _jsx("div", { style: sectionTitle, children: t("workspacesTitle") }), state.workspaces.length === 0 ? _jsx("p", { style: hint, children: t("none") }) : (state.workspaces.map((workspace) => (_jsx(WorkspaceSection, { t: t, workspace: workspace, containers: state.containers.filter((container) => container.workspaceSlug === workspace.workspaceSlug), images: state.images, volumes: state.volumes, secrets: state.secrets, busy: state.busy, defaultImage: state.defaultImage, projectsRoot: state.projectsRoot, directoryPicker: state.directoryPicker, onRemove: props.remove, onRemoveWorkspace: props.removeWorkspace, onRecreate: props.recreate, onCreate: props.createContainer, onStartContainer: props.startContainer, onAddContainerMount: props.addContainerMount, onRemoveContainerMount: props.removeContainerMount, onUpdateContainerMount: props.updateContainerMount, onSetContainerPaths: props.setContainerPaths, onAddContainerSecret: props.addContainerSecret, onRemoveContainerSecret: props.removeContainerSecret, onSyncDefaults: props.syncDefaultEnv, defaultEnvCount: Object.keys(state.containerEnv).length }, workspace.workspaceSlug)))), _jsxs("div", { style: {
|
|
78
|
+
display: "flex",
|
|
79
|
+
flexWrap: "wrap",
|
|
80
|
+
alignItems: "center",
|
|
81
|
+
gap: "8px",
|
|
82
|
+
}, children: [_jsx("div", { style: { ...sectionTitle, flex: 1 }, children: t("imagesTitle") }), _jsx(Button, { variant: "outline", size: "sm", disabled: state.busy, onClick: () => {
|
|
83
|
+
setDefaultImage(state.defaultImage);
|
|
84
|
+
setDefaultOpen(true);
|
|
85
|
+
}, children: t("setDefaultImage") }), _jsx(Button, { variant: "outline", size: "sm", disabled: state.busy, onClick: () => {
|
|
61
86
|
setImageId("");
|
|
62
|
-
setParent("");
|
|
87
|
+
setParent(baseImages[0]?.imageId ?? customImages[0]?.imageId ?? "");
|
|
63
88
|
setPackages([]);
|
|
64
|
-
setBuildOpen(
|
|
65
|
-
} }), _jsx(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
setBuildOpen(true);
|
|
90
|
+
}, children: t("buildImage") }), _jsx(ConfirmButton, { t: t, label: t("rebuildAllImages"), title: t("confirmTitle"), description: t("confirmRebuildAllImages"), disabled: state.busy, onConfirm: props.rebuildAllImages })] }), _jsx("div", { style: sectionTitle, children: t("baseImagesTitle") }), baseImages.length === 0 ? _jsx("p", { style: hint, children: t("none") }) : (baseImages.map((image) => (_jsx(BaseImageRow, { t: t, image: image, busy: state.busy, defaultImage: state.defaultImage, onRebuild: props.rebuildBaseImage, onPull: props.pullBaseImage, onSetDefault: props.setDefaultImage }, image.imageId)))), _jsx("div", { style: sectionTitle, children: t("customImagesTitle") }), customImages.length === 0 ? _jsx("p", { style: hint, children: t("none") }) : (customImages.map((image) => (_jsx(ImageItem, { t: t, image: image, busy: state.busy, defaultImage: state.defaultImage, onRemove: props.removeImage, onRebuild: props.rebuildImage, onSetDefault: props.setDefaultImage }, image.imageId)))), _jsx(ImageBuildModal, { t: t, images: state.images, open: buildOpen, imageId: imageId, parent: parent, packages: packages, onImageId: setImageId, onParent: setParent, onPackages: setPackages, onClose: () => setBuildOpen(false), onBuild: () => {
|
|
91
|
+
props.buildImage(imageId.trim(), parent.trim(), packages);
|
|
92
|
+
setImageId("");
|
|
93
|
+
setParent("");
|
|
94
|
+
setPackages([]);
|
|
95
|
+
setBuildOpen(false);
|
|
96
|
+
} }), _jsxs(Modal, { open: defaultOpen, onClose: () => setDefaultOpen(false), title: t("setDefaultImageTitle"), closeLabel: t("cancel"), footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setDefaultOpen(false), children: t("cancel") }), _jsx(Button, { variant: "primary", size: "sm", disabled: defaultImage === "", onClick: () => {
|
|
97
|
+
props.setDefaultImage(defaultImage);
|
|
98
|
+
setDefaultOpen(false);
|
|
99
|
+
}, children: t("setDefaultImage") })] }), children: [_jsxs("select", { style: imageSelect, value: defaultImage, onChange: (event) => setDefaultImage(event.target.value), children: [defaultCandidates.length === 0
|
|
100
|
+
? _jsx("option", { value: "", children: t("none") })
|
|
101
|
+
: null, defaultCandidates.map((name) => (_jsx("option", { value: name, children: name }, name)))] }), _jsx("p", { style: hint, children: t("defaultImageHint") })] }), _jsx(CachesSection, { t: t, caches: state.caches, busy: state.busy, onClean: props.cleanCaches }), _jsx(VolumesSection, { t: t, volumes: state.volumes, busy: state.busy, writable: state.writable, onCreate: props.createVolume, onRemove: props.removeVolume }), _jsx(SecretsSection, { t: t, secrets: state.secrets, busy: state.busy, writable: state.writable, onCreate: props.createSecret, onRemove: props.removeSecret, onSet: props.setSecret }), _jsx(DefaultEnvironmentSection, { t: t, env: state.containerEnv, busy: state.busy, writable: state.writable, onSave: props.saveContainerEnv, onSync: props.syncDefaultEnv }), _jsxs("div", { style: {
|
|
102
|
+
display: "flex",
|
|
103
|
+
flexWrap: "wrap",
|
|
104
|
+
alignItems: "center",
|
|
105
|
+
gap: "8px",
|
|
106
|
+
}, children: [_jsx("div", { style: { ...sectionTitle, flex: 1 }, children: t("versionsTitle") }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => void copyVersions(), children: copyState === "copied"
|
|
107
|
+
? t("copied")
|
|
108
|
+
: copyState === "failed"
|
|
109
|
+
? t("copyFailed")
|
|
110
|
+
: t("copyVersions") })] }), _jsxs("div", { style: {
|
|
111
|
+
...hint,
|
|
112
|
+
display: "flex",
|
|
113
|
+
flexDirection: "column",
|
|
114
|
+
gap: "2px",
|
|
115
|
+
margin: 0,
|
|
116
|
+
}, children: [_jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("dsh") }), state.dshVersion
|
|
117
|
+
? (` ${state.dshVersion}`)
|
|
118
|
+
: (_jsxs(_Fragment, { children: [" ", _jsx("span", { style: { color: "var(--dsw-alias-state-error-primary)" }, children: t("versionUnknown") })] }))] }), _jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("cardTitle") }), state.version ? ` ${state.version}` : "", state.commit ? ` · ${state.commit}` : ""] }), _jsxs("span", { children: [_jsx("span", { style: versionLabel, children: t("orchestrator") }), state.orchestratorVersion
|
|
119
|
+
? (` ${state.orchestratorVersion}`)
|
|
120
|
+
: (_jsxs(_Fragment, { children: [" ", _jsx("span", { style: { color: "var(--dsw-alias-state-error-primary)" }, children: t("versionUnknown") })] }))] })] }), _jsx("div", { style: footerRow, children: _jsx(Button, { variant: "outline", size: "sm", disabled: state.busy, onClick: props.reload, children: state.busy ? t("busy") : t("reload") }) })] }));
|
|
79
121
|
}
|
|
@@ -7,7 +7,7 @@ export interface MountInput {
|
|
|
7
7
|
volume: string;
|
|
8
8
|
secret: string;
|
|
9
9
|
}
|
|
10
|
-
export type CommandOp = "remove" | "workspace_remove" | "recreate" | "create" | "volume_create" | "volume_remove" | "image_remove" | "secret_create" | "secret_remove" | "secret_set" | "image_rebuild" | "image_rebuild_all" | "image_build" | "image_base_rebuild" | "image_base_pull" | "container_secret_add" | "container_secret_remove" | "container_mount_add" | "container_mount_remove" | "container_mount_update" | "container_path_set" | "cache_clean";
|
|
10
|
+
export type CommandOp = "remove" | "workspace_remove" | "recreate" | "create" | "volume_create" | "volume_remove" | "image_remove" | "secret_create" | "secret_remove" | "secret_set" | "image_rebuild" | "image_rebuild_all" | "image_build" | "image_base_rebuild" | "image_base_pull" | "container_secret_add" | "container_secret_remove" | "container_mount_add" | "container_mount_remove" | "container_mount_update" | "container_path_set" | "default_env_sync" | "cache_clean";
|
|
11
11
|
export interface CommandRequest {
|
|
12
12
|
op: CommandOp;
|
|
13
13
|
workspace: string;
|
|
@@ -19,11 +19,11 @@ export interface CommandRequest {
|
|
|
19
19
|
env: Record<string, string>;
|
|
20
20
|
container: string;
|
|
21
21
|
secret: string;
|
|
22
|
-
|
|
22
|
+
secretEnvName: string;
|
|
23
23
|
length: number;
|
|
24
24
|
charset: string;
|
|
25
25
|
packages: string[];
|
|
26
|
-
|
|
26
|
+
secretEnv: Record<string, string>;
|
|
27
27
|
cacheMode: string;
|
|
28
28
|
mount: MountInput | null;
|
|
29
29
|
}
|
|
@@ -85,6 +85,9 @@ export interface WorkspaceView {
|
|
|
85
85
|
export interface CardSnapshot {
|
|
86
86
|
version: string;
|
|
87
87
|
commit: string;
|
|
88
|
+
dshVersion: string;
|
|
89
|
+
orchestratorVersion: string;
|
|
90
|
+
versionState: "ok" | "minor-mismatch" | "major-mismatch";
|
|
88
91
|
projectsRoot: string;
|
|
89
92
|
workspaces: readonly WorkspaceView[];
|
|
90
93
|
containers: readonly ContainerView[];
|
|
@@ -22,10 +22,15 @@ function formatBytes(bytes) {
|
|
|
22
22
|
export function CachesSection(props) {
|
|
23
23
|
const { t, caches, busy, onClean } = props;
|
|
24
24
|
const disabled = busy || caches.length === 0;
|
|
25
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { style: sectionTitle, children: t("cachesTitle") }), _jsxs("div", { style: wsBody, children: [caches.length === 0
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { style: sectionTitle, children: t("cachesTitle") }), _jsxs("div", { style: wsBody, children: [caches.length === 0
|
|
26
|
+
? _jsx("p", { style: { ...hint, margin: 0 }, children: t("noCaches") })
|
|
27
|
+
: (caches.map((cache) => (_jsxs("div", { style: {
|
|
28
|
+
display: "flex",
|
|
29
|
+
flexWrap: "wrap",
|
|
30
|
+
alignItems: "center",
|
|
31
|
+
gap: "8px",
|
|
32
|
+
}, children: [_jsx("strong", { style: { color: "var(--dsw-alias-label-primary)" }, children: cache.manager }), _jsx("span", { style: hint, children: t("cacheUsage", {
|
|
33
|
+
size: formatBytes(cache.bytes),
|
|
34
|
+
n: cache.files,
|
|
35
|
+
}) })] }, cache.manager)))), _jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: "8px" }, children: [_jsx(ConfirmButton, { t: t, label: t("cacheKeepLatest"), title: t("confirmTitle"), description: t("confirmCacheKeepLatest"), disabled: disabled, onConfirm: () => onClean("keep-latest") }), _jsx(ConfirmButton, { t: t, label: t("cacheRemoveAll"), title: t("confirmTitle"), description: t("confirmCacheRemoveAll"), disabled: disabled, onConfirm: () => onClean("all") })] })] })] }));
|
|
31
36
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type SnapshotStore } from "@deepseek-ai/dsh-client-store";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ConfigForm, ConfigFormSnapshot } from "@deepseek-ai/dsh-client-ui-settings/client";
|
|
3
3
|
import type { CacheView, ContainerView, ImageView, MountInput, SecretView, VolumeView, WorkspaceView } from "./card-protocol.js";
|
|
4
4
|
import { type CardClient } from "./card-client.js";
|
|
5
5
|
import { type DirectoryPickerFace } from "./directory-picker.js";
|
|
6
6
|
export declare const CONTAINER_NS = "podman";
|
|
7
7
|
export interface ContainerSettings {
|
|
8
8
|
defaultImage: string;
|
|
9
|
-
socketsRoot: string;
|
|
10
9
|
uiLocale?: string;
|
|
10
|
+
containerEnv?: Record<string, string>;
|
|
11
11
|
}
|
|
12
12
|
export interface ProjectMountView {
|
|
13
13
|
projectName: string;
|
|
@@ -32,10 +32,11 @@ export interface CardState {
|
|
|
32
32
|
notice: string;
|
|
33
33
|
version: string;
|
|
34
34
|
commit: string;
|
|
35
|
+
dshVersion: string;
|
|
36
|
+
orchestratorVersion: string;
|
|
37
|
+
versionState: "ok" | "minor-mismatch" | "major-mismatch";
|
|
35
38
|
defaultImage: string;
|
|
36
|
-
|
|
37
|
-
socketsRoot: string;
|
|
38
|
-
socketsRootDraft: string;
|
|
39
|
+
containerEnv: Record<string, string>;
|
|
39
40
|
projectsRoot: string;
|
|
40
41
|
directoryPicker?: DirectoryPickerFace;
|
|
41
42
|
workspaces: readonly WorkspaceView[];
|
|
@@ -50,10 +51,10 @@ export interface ContainerCardFace {
|
|
|
50
51
|
containerCard: SnapshotStore<CardState>;
|
|
51
52
|
};
|
|
52
53
|
reload: () => void;
|
|
53
|
-
remove: (workspace: string) => void;
|
|
54
|
+
remove: (workspace: string, container: string) => void;
|
|
54
55
|
cleanCaches: (mode: string) => void;
|
|
55
56
|
removeWorkspace: (workspace: string) => void;
|
|
56
|
-
recreate: (workspace: string, image: string, env?: Record<string, string>) => void;
|
|
57
|
+
recreate: (workspace: string, container: string, image: string, env?: Record<string, string>) => void;
|
|
57
58
|
createContainer: (workspace: WorkspaceView, config?: ContainerCreateConfig) => void;
|
|
58
59
|
startContainer: (workspace: WorkspaceView, container: string, config?: ContainerCreateConfig) => void;
|
|
59
60
|
addContainerMount: (workspace: string, container: string, mount: MountInput) => void;
|
|
@@ -72,14 +73,10 @@ export interface ContainerCardFace {
|
|
|
72
73
|
createSecret: (name: string, length?: number, charset?: string) => void;
|
|
73
74
|
removeSecret: (name: string) => void;
|
|
74
75
|
setSecret: (name: string, value: string) => void;
|
|
75
|
-
addContainerSecret: (workspace: string, envVar: string, secret: string) => void;
|
|
76
|
-
removeContainerSecret: (workspace: string, envVar: string) => void;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
discardDefaultImage: () => void;
|
|
80
|
-
editSocketsRoot: (text: string) => void;
|
|
81
|
-
saveSocketsRoot: () => void;
|
|
82
|
-
discardSocketsRoot: () => void;
|
|
76
|
+
addContainerSecret: (workspace: string, container: string, envVar: string, secret: string) => void;
|
|
77
|
+
removeContainerSecret: (workspace: string, container: string, envVar: string) => void;
|
|
78
|
+
saveContainerEnv: (env: Record<string, string>) => void;
|
|
79
|
+
syncDefaultEnv: (workspace: string) => void;
|
|
83
80
|
}
|
|
84
81
|
export declare class ContainerCardController {
|
|
85
82
|
private readonly scope;
|
|
@@ -87,10 +84,11 @@ export declare class ContainerCardController {
|
|
|
87
84
|
private readonly drafts;
|
|
88
85
|
private readonly client;
|
|
89
86
|
private snapshot;
|
|
90
|
-
private
|
|
87
|
+
private pending;
|
|
88
|
+
private generation;
|
|
91
89
|
private notice;
|
|
92
90
|
private directoryPicker;
|
|
93
|
-
constructor(scope:
|
|
91
|
+
constructor(scope: ConfigForm<ContainerSettings>, client?: CardClient);
|
|
94
92
|
private draft;
|
|
95
93
|
private project;
|
|
96
94
|
private publish;
|
|
@@ -104,4 +102,4 @@ export declare class ContainerCardController {
|
|
|
104
102
|
inject(): ContainerCardFace;
|
|
105
103
|
private defaultImage;
|
|
106
104
|
}
|
|
107
|
-
export type {
|
|
105
|
+
export type { ConfigFormSnapshot };
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
2
2
|
//
|
|
3
3
|
// SPDX-License-Identifier: MIT
|
|
4
|
-
import { createSnapshotStore } from "@deepseek-ai/dsh-client-store";
|
|
4
|
+
import { createSnapshotStore, } from "@deepseek-ai/dsh-client-store";
|
|
5
5
|
import { createCardClient } from "./card-client.js";
|
|
6
6
|
export const CONTAINER_NS = "podman";
|
|
7
7
|
const EMPTY_SNAPSHOT = {
|
|
8
8
|
version: "",
|
|
9
9
|
commit: "",
|
|
10
|
+
dshVersion: "",
|
|
11
|
+
orchestratorVersion: "",
|
|
12
|
+
versionState: "ok",
|
|
10
13
|
projectsRoot: "",
|
|
11
14
|
workspaces: [],
|
|
12
15
|
containers: [],
|
|
@@ -24,7 +27,10 @@ export class ContainerCardController {
|
|
|
24
27
|
drafts = new Map();
|
|
25
28
|
client;
|
|
26
29
|
snapshot = EMPTY_SNAPSHOT;
|
|
27
|
-
busy
|
|
30
|
+
// Counts in-flight commands, so overlapping ones cannot clear the busy state
|
|
31
|
+
// early; generation makes a superseded response drop its stale snapshot.
|
|
32
|
+
pending = 0;
|
|
33
|
+
generation = 0;
|
|
28
34
|
notice = "";
|
|
29
35
|
directoryPicker;
|
|
30
36
|
constructor(scope, client = createCardClient()) {
|
|
@@ -45,14 +51,15 @@ export class ContainerCardController {
|
|
|
45
51
|
return {
|
|
46
52
|
available: snapshot.status === "ready" && value !== undefined,
|
|
47
53
|
writable: snapshot.writable,
|
|
48
|
-
busy: this.
|
|
54
|
+
busy: this.pending > 0,
|
|
49
55
|
notice: this.notice,
|
|
50
56
|
version: this.snapshot.version,
|
|
51
57
|
commit: this.snapshot.commit,
|
|
58
|
+
dshVersion: this.snapshot.dshVersion,
|
|
59
|
+
orchestratorVersion: this.snapshot.orchestratorVersion,
|
|
60
|
+
versionState: this.snapshot.versionState,
|
|
52
61
|
defaultImage: value?.defaultImage ?? "",
|
|
53
|
-
|
|
54
|
-
socketsRoot: value?.socketsRoot ?? "",
|
|
55
|
-
socketsRootDraft: this.draft("socketsRoot", value?.socketsRoot ?? ""),
|
|
62
|
+
containerEnv: value?.containerEnv ?? {},
|
|
56
63
|
projectsRoot: this.snapshot.projectsRoot,
|
|
57
64
|
...(this.directoryPicker === undefined
|
|
58
65
|
? {}
|
|
@@ -86,43 +93,62 @@ export class ContainerCardController {
|
|
|
86
93
|
env: extra.env ?? {},
|
|
87
94
|
container: extra.container ?? "",
|
|
88
95
|
secret: extra.secret ?? "",
|
|
89
|
-
|
|
96
|
+
secretEnvName: extra.secretEnvName ?? "",
|
|
90
97
|
length: extra.length ?? 0,
|
|
91
98
|
charset: extra.charset ?? "",
|
|
92
99
|
packages: extra.packages ?? [],
|
|
93
|
-
|
|
100
|
+
secretEnv: extra.secretEnv ?? {},
|
|
94
101
|
cacheMode: extra.cacheMode ?? "",
|
|
95
102
|
mount: extra.mount ?? null,
|
|
96
103
|
});
|
|
97
104
|
}
|
|
98
105
|
async dispatch(request) {
|
|
99
|
-
|
|
106
|
+
const generation = ++this.generation;
|
|
107
|
+
this.pending++;
|
|
100
108
|
this.notice = "";
|
|
101
109
|
this.publish();
|
|
102
110
|
try {
|
|
103
111
|
const result = await this.client.command(request);
|
|
112
|
+
const snapshot = await this.client.snapshot();
|
|
113
|
+
// A reload (or another command) started after this one wins: applying
|
|
114
|
+
// this response would overwrite the fresher state with a stale snapshot.
|
|
115
|
+
if (generation !== this.generation)
|
|
116
|
+
return;
|
|
104
117
|
this.notice = result.notice ?? "";
|
|
105
|
-
this.snapshot =
|
|
118
|
+
this.snapshot = snapshot;
|
|
106
119
|
}
|
|
107
120
|
catch (error) {
|
|
108
|
-
|
|
121
|
+
if (generation === this.generation) {
|
|
122
|
+
this.notice = error instanceof Error ? error.message : String(error);
|
|
123
|
+
}
|
|
109
124
|
}
|
|
110
125
|
finally {
|
|
111
|
-
this.
|
|
126
|
+
this.pending--;
|
|
112
127
|
this.publish();
|
|
113
128
|
}
|
|
114
129
|
}
|
|
115
130
|
async reload() {
|
|
131
|
+
const generation = ++this.generation;
|
|
132
|
+
this.pending++;
|
|
133
|
+
this.publish();
|
|
116
134
|
try {
|
|
117
|
-
|
|
135
|
+
const snapshot = await this.client.snapshot();
|
|
136
|
+
if (generation !== this.generation)
|
|
137
|
+
return;
|
|
138
|
+
this.snapshot = snapshot;
|
|
118
139
|
// A refresh clears the previous action's notice, so reopening the
|
|
119
140
|
// settings does not replay feedback from an earlier session.
|
|
120
141
|
this.notice = "";
|
|
121
142
|
}
|
|
122
143
|
catch (error) {
|
|
123
|
-
|
|
144
|
+
if (generation === this.generation) {
|
|
145
|
+
this.notice = error instanceof Error ? error.message : String(error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
this.pending--;
|
|
150
|
+
this.publish();
|
|
124
151
|
}
|
|
125
|
-
this.publish();
|
|
126
152
|
}
|
|
127
153
|
edit(field, text) {
|
|
128
154
|
this.drafts.set(field, text);
|
|
@@ -144,27 +170,55 @@ export class ContainerCardController {
|
|
|
144
170
|
reload: () => {
|
|
145
171
|
void this.reload();
|
|
146
172
|
},
|
|
147
|
-
remove: (workspace) => this.command("remove", workspace, ""),
|
|
173
|
+
remove: (workspace, container) => this.command("remove", workspace, "", { container }),
|
|
148
174
|
cleanCaches: (mode) => this.command("cache_clean", "", "", { cacheMode: mode }),
|
|
149
175
|
removeWorkspace: (workspace) => this.command("workspace_remove", workspace, ""),
|
|
150
|
-
recreate: (workspace, image, env) => this.command("recreate", workspace, image, {
|
|
176
|
+
recreate: (workspace, container, image, env) => this.command("recreate", workspace, image, {
|
|
177
|
+
container,
|
|
178
|
+
...(env ? { env } : {}),
|
|
179
|
+
}),
|
|
151
180
|
createContainer: (workspace, config) => this.command("create", workspace.workspaceSlug, config?.image ?? (workspace.imageId || this.defaultImage() || ""), {
|
|
152
181
|
projectName: workspace.projectName,
|
|
153
|
-
...(config?.mounts && config.mounts.length > 0
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
...(config?.
|
|
182
|
+
...(config?.mounts && config.mounts.length > 0
|
|
183
|
+
? { mounts: config.mounts }
|
|
184
|
+
: {}),
|
|
185
|
+
...(config?.env && Object.keys(config.env).length > 0
|
|
186
|
+
? { env: config.env }
|
|
187
|
+
: {}),
|
|
188
|
+
...(config?.paths && config.paths.length > 0
|
|
189
|
+
? { paths: config.paths }
|
|
190
|
+
: {}),
|
|
191
|
+
...(config?.secretEnv && Object.keys(config.secretEnv).length > 0
|
|
192
|
+
? { secretEnv: config.secretEnv }
|
|
193
|
+
: {}),
|
|
157
194
|
}),
|
|
158
195
|
startContainer: (workspace, container, config) => this.command("create", workspace.workspaceSlug, config?.image ?? (workspace.imageId || this.defaultImage() || ""), {
|
|
159
196
|
container,
|
|
160
|
-
...(config?.mounts && config.mounts.length > 0
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
...(config?.
|
|
197
|
+
...(config?.mounts && config.mounts.length > 0
|
|
198
|
+
? { mounts: config.mounts }
|
|
199
|
+
: {}),
|
|
200
|
+
...(config?.env && Object.keys(config.env).length > 0
|
|
201
|
+
? { env: config.env }
|
|
202
|
+
: {}),
|
|
203
|
+
...(config?.paths && config.paths.length > 0
|
|
204
|
+
? { paths: config.paths }
|
|
205
|
+
: {}),
|
|
206
|
+
...(config?.secretEnv && Object.keys(config.secretEnv).length > 0
|
|
207
|
+
? { secretEnv: config.secretEnv }
|
|
208
|
+
: {}),
|
|
209
|
+
}),
|
|
210
|
+
addContainerMount: (workspace, container, mount) => this.command("container_mount_add", workspace, "", {
|
|
211
|
+
container,
|
|
212
|
+
mount,
|
|
213
|
+
}),
|
|
214
|
+
removeContainerMount: (workspace, container, mount) => this.command("container_mount_remove", workspace, "", {
|
|
215
|
+
container,
|
|
216
|
+
mount,
|
|
217
|
+
}),
|
|
218
|
+
updateContainerMount: (workspace, container, mount) => this.command("container_mount_update", workspace, "", {
|
|
219
|
+
container,
|
|
220
|
+
mount,
|
|
164
221
|
}),
|
|
165
|
-
addContainerMount: (workspace, container, mount) => this.command("container_mount_add", workspace, "", { container, mount }),
|
|
166
|
-
removeContainerMount: (workspace, container, mount) => this.command("container_mount_remove", workspace, "", { container, mount }),
|
|
167
|
-
updateContainerMount: (workspace, container, mount) => this.command("container_mount_update", workspace, "", { container, mount }),
|
|
168
222
|
setContainerPaths: (workspace, container, paths) => this.command("container_path_set", workspace, "", { container, paths }),
|
|
169
223
|
createVolume: (name) => this.command("volume_create", name, ""),
|
|
170
224
|
removeVolume: (name) => this.command("volume_remove", name, ""),
|
|
@@ -184,21 +238,19 @@ export class ContainerCardController {
|
|
|
184
238
|
}),
|
|
185
239
|
removeSecret: (name) => this.command("secret_remove", name, ""),
|
|
186
240
|
setSecret: (name, value) => this.command("secret_set", name, "", { value }),
|
|
187
|
-
addContainerSecret: (workspace, envVar, secret) => this.command("container_secret_add", workspace, "", {
|
|
188
|
-
container
|
|
189
|
-
|
|
241
|
+
addContainerSecret: (workspace, container, envVar, secret) => this.command("container_secret_add", workspace, "", {
|
|
242
|
+
container,
|
|
243
|
+
secretEnvName: envVar,
|
|
190
244
|
secret,
|
|
191
245
|
}),
|
|
192
|
-
removeContainerSecret: (workspace, envVar) => this.command("container_secret_remove", workspace, "", {
|
|
193
|
-
container
|
|
194
|
-
|
|
246
|
+
removeContainerSecret: (workspace, container, envVar) => this.command("container_secret_remove", workspace, "", {
|
|
247
|
+
container,
|
|
248
|
+
secretEnvName: envVar,
|
|
195
249
|
}),
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
saveSocketsRoot: () => this.save("socketsRoot"),
|
|
201
|
-
discardSocketsRoot: () => this.discard("socketsRoot"),
|
|
250
|
+
saveContainerEnv: (env) => {
|
|
251
|
+
void this.scope.set("containerEnv", env);
|
|
252
|
+
},
|
|
253
|
+
syncDefaultEnv: (workspace) => this.command("default_env_sync", workspace, ""),
|
|
202
254
|
};
|
|
203
255
|
}
|
|
204
256
|
defaultImage() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type ContainerCreateConfig, type ImageView, type WorkspaceView } from "./container-card-controller.js";
|
|
2
2
|
import { type DirectoryPickerFace } from "./directory-picker.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { Translate } from "./locales.js";
|
|
4
4
|
import { type ReactNode } from "react";
|
|
5
5
|
export declare function CreateContainerModal(props: {
|
|
6
|
-
t:
|
|
6
|
+
t: Translate;
|
|
7
7
|
workspace: WorkspaceView;
|
|
8
8
|
images: readonly ImageView[];
|
|
9
9
|
volumes: readonly {
|
|
@@ -2,7 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { EnvEditor, MountsEditor } from "./container-card-editors.js";
|
|
3
3
|
import { PathsList } from "./container-card-paths.js";
|
|
4
4
|
import { Field, namePattern, sanitizeName } from "./container-card-shared.js";
|
|
5
|
-
import { greyId, hint, imageSelect, sectionTitle } from "./container-card-styles.js";
|
|
5
|
+
import { greyId, hint, imageSelect, sectionTitle, } from "./container-card-styles.js";
|
|
6
6
|
import { Button, Input, Modal } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
7
7
|
import { useEffect, useId, useState } from "react";
|
|
8
8
|
export function CreateContainerModal(props) {
|
|
@@ -81,9 +81,15 @@ export function CreateContainerModal(props) {
|
|
|
81
81
|
display: "flex",
|
|
82
82
|
flexDirection: "column",
|
|
83
83
|
gap: "12px",
|
|
84
|
-
}, children: [_jsx(Field, { label: t("image"), htmlFor: imageLabel, children: _jsxs("select", { id: imageLabel, style: imageSelect, value: image, disabled: busy, onChange: (event) => setImage(event.target.value), children: [candidates.length === 0
|
|
85
|
-
? t("
|
|
86
|
-
:
|
|
84
|
+
}, children: [_jsx(Field, { label: t("image"), htmlFor: imageLabel, children: _jsxs("select", { id: imageLabel, style: imageSelect, value: image, disabled: busy, onChange: (event) => setImage(event.target.value), children: [candidates.length === 0
|
|
85
|
+
? _jsx("option", { value: "", children: t("none") })
|
|
86
|
+
: null, candidates.map((imageId) => (_jsx("option", { value: imageId, children: imageId }, imageId)))] }) }), named
|
|
87
|
+
? (_jsxs(Field, { label: t("containerName"), htmlFor: nameLabel, children: [_jsx(Input, { id: nameLabel, value: name, disabled: busy, onChange: (event) => setName(sanitizeName(event.target.value)) }), name !== "" && !validName
|
|
88
|
+
? (_jsx("p", { style: { ...hint, margin: 0 }, children: nameTaken
|
|
89
|
+
? t("containerNameTaken")
|
|
90
|
+
: t("invalidContainerName") }))
|
|
91
|
+
: null] }))
|
|
92
|
+
: null, _jsx("div", { style: sectionTitle, children: t("envTitle") }), _jsx(EnvEditor, { t: t, env: env, busy: busy, onChange: setEnv }), _jsx("div", { style: sectionTitle, children: t("mountsTitle") }), _jsx(MountsEditor, { t: t, mounts: mounts, volumes: volumes, secrets: secrets, busy: busy, enabled: !busy, projectsRoot: projectsRoot, directoryPicker: directoryPicker, modeControl: "select", onAdd: (mount) => setMounts([...mounts, mount]), onRemove: (mount) => setMounts(mounts.filter((item) => item !== mount)), onUpdate: (updated, index) => setMounts(mounts.map((item, at) => (at === index ? updated : item))) }), _jsx("div", { style: sectionTitle, children: t("pathsTitle") }), _jsx(PathsList, { t: t, entries: paths, busy: busy, onChange: setPaths }), _jsx("div", { style: sectionTitle, children: t("containerSecretsTitle") }), Object.entries(secretEnv).map(([envVar, secretName]) => (_jsxs("div", { style: {
|
|
87
93
|
display: "flex",
|
|
88
94
|
flexWrap: "wrap",
|
|
89
95
|
alignItems: "center",
|
|
@@ -102,7 +108,9 @@ export function CreateContainerModal(props) {
|
|
|
102
108
|
flexWrap: "wrap",
|
|
103
109
|
alignItems: "center",
|
|
104
110
|
gap: "8px",
|
|
105
|
-
}, children: [_jsxs("select", { style: imageSelect, value: attachSecret, disabled: busy, onChange: (event) => setAttachSecret(event.target.value), "aria-label": t("attachSecret"), children: [secrets.length === 0
|
|
111
|
+
}, children: [_jsxs("select", { style: imageSelect, value: attachSecret, disabled: busy, onChange: (event) => setAttachSecret(event.target.value), "aria-label": t("attachSecret"), children: [secrets.length === 0
|
|
112
|
+
? _jsx("option", { value: "", children: t("none") })
|
|
113
|
+
: null, secrets.map((secret) => (_jsx("option", { value: secret.name, children: secret.name }, secret.name)))] }), _jsx(Input, { value: attachVar, disabled: busy, placeholder: t("secretEnvName"), onChange: (event) => setAttachVar(event.target.value), onKeyDown: (event) => {
|
|
106
114
|
if (event.key === "Enter")
|
|
107
115
|
attach();
|
|
108
116
|
}, style: { width: "200px" } }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy || attachVar.trim() === "" || attachSecret === "", onClick: attach, children: t("attachSecret") })] })] }) }));
|