@exagone313/dsh-podman 0.2.0-rc.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.
- package/LICENSE +21 -0
- package/README.md +41 -0
- package/README.zh.md +41 -0
- package/cordis.patch.yml +17 -0
- package/dist/approval-reasons.d.ts +129 -0
- package/dist/approval-reasons.js +253 -0
- package/dist/approval-reasons.test.d.ts +1 -0
- package/dist/approval-reasons.test.js +115 -0
- package/dist/approval.d.ts +29 -0
- package/dist/approval.js +412 -0
- package/dist/approval.test.d.ts +1 -0
- package/dist/approval.test.js +462 -0
- package/dist/card-route.d.ts +15 -0
- package/dist/card-route.js +478 -0
- package/dist/card-route.test.d.ts +1 -0
- package/dist/card-route.test.js +181 -0
- package/dist/card-test-support.d.ts +15 -0
- package/dist/card-test-support.js +160 -0
- package/dist/client/ContainerCard.d.ts +6 -0
- package/dist/client/ContainerCard.js +79 -0
- package/dist/client/card-client.d.ts +6 -0
- package/dist/client/card-client.js +47 -0
- package/dist/client/card-protocol.d.ts +101 -0
- package/dist/client/card-protocol.js +10 -0
- package/dist/client/container-card-caches.d.ts +10 -0
- package/dist/client/container-card-caches.js +31 -0
- package/dist/client/container-card-controller.d.ts +107 -0
- package/dist/client/container-card-controller.js +207 -0
- package/dist/client/container-card-create-modal.d.ts +24 -0
- package/dist/client/container-card-create-modal.js +109 -0
- package/dist/client/container-card-directory-styles.d.ts +5 -0
- package/dist/client/container-card-directory-styles.js +246 -0
- package/dist/client/container-card-directory.d.ts +12 -0
- package/dist/client/container-card-directory.js +144 -0
- package/dist/client/container-card-editors.d.ts +30 -0
- package/dist/client/container-card-editors.js +116 -0
- package/dist/client/container-card-images.d.ts +37 -0
- package/dist/client/container-card-images.js +35 -0
- package/dist/client/container-card-paths.d.ts +20 -0
- package/dist/client/container-card-paths.js +88 -0
- package/dist/client/container-card-row.d.ts +30 -0
- package/dist/client/container-card-row.js +54 -0
- package/dist/client/container-card-secrets.d.ts +21 -0
- package/dist/client/container-card-secrets.js +71 -0
- package/dist/client/container-card-shared.d.ts +55 -0
- package/dist/client/container-card-shared.js +152 -0
- package/dist/client/container-card-styles.d.ts +21 -0
- package/dist/client/container-card-styles.js +143 -0
- package/dist/client/container-card-volumes.d.ts +12 -0
- package/dist/client/container-card-volumes.js +37 -0
- package/dist/client/container-card-workspace.d.ts +31 -0
- package/dist/client/container-card-workspace.js +24 -0
- package/dist/client/directory-picker.d.ts +12 -0
- package/dist/client/directory-picker.js +30 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +4419 -0
- package/dist/client/locales.d.ts +9 -0
- package/dist/client/locales.js +370 -0
- package/dist/client/read-only-approval.d.ts +8 -0
- package/dist/client/read-only-approval.js +63 -0
- package/dist/client/slot-contract.d.ts +12 -0
- package/dist/client/slot-contract.js +4 -0
- package/dist/client/terminal-styles.d.ts +5 -0
- package/dist/client/terminal-styles.js +31 -0
- package/dist/client/tool-views.d.ts +8 -0
- package/dist/client/tool-views.js +241 -0
- package/dist/containers.test.d.ts +1 -0
- package/dist/containers.test.js +443 -0
- package/dist/daemons.test.d.ts +1 -0
- package/dist/daemons.test.js +153 -0
- package/dist/fs-provider.d.ts +23 -0
- package/dist/fs-provider.js +184 -0
- package/dist/fs-provider.test.d.ts +1 -0
- package/dist/fs-provider.test.js +260 -0
- package/dist/generated/version.d.ts +2 -0
- package/dist/generated/version.js +6 -0
- package/dist/grpc/proto/dshctl/v1/control.proto +133 -0
- package/dist/grpc/proto/dshguest/v1/guest.proto +79 -0
- package/dist/grpc/runtime-client.d.ts +5 -0
- package/dist/grpc/runtime-client.js +43 -0
- package/dist/guest-rpc.d.ts +62 -0
- package/dist/guest-rpc.js +391 -0
- package/dist/images.test.d.ts +1 -0
- package/dist/images.test.js +79 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +140 -0
- package/dist/misc.test.d.ts +1 -0
- package/dist/misc.test.js +141 -0
- package/dist/mount-enums.d.ts +22 -0
- package/dist/mount-enums.js +81 -0
- package/dist/mount-input.d.ts +14 -0
- package/dist/mount-input.js +64 -0
- package/dist/mounts.test.d.ts +1 -0
- package/dist/mounts.test.js +408 -0
- package/dist/output-reader.d.ts +34 -0
- package/dist/output-reader.js +87 -0
- package/dist/paths.test.d.ts +1 -0
- package/dist/paths.test.js +74 -0
- package/dist/preferences.d.ts +6 -0
- package/dist/preferences.js +27 -0
- package/dist/project-path.d.ts +6 -0
- package/dist/project-path.js +73 -0
- package/dist/project-path.test.d.ts +1 -0
- package/dist/project-path.test.js +52 -0
- package/dist/prompts.d.ts +13 -0
- package/dist/prompts.js +149 -0
- package/dist/public.d.ts +4 -0
- package/dist/public.js +63 -0
- package/dist/read-only-shell.d.ts +14 -0
- package/dist/read-only-shell.js +150 -0
- package/dist/read-only-shell.test.d.ts +1 -0
- package/dist/read-only-shell.test.js +127 -0
- package/dist/secrets.test.d.ts +1 -0
- package/dist/secrets.test.js +181 -0
- package/dist/settings-commands.test.d.ts +1 -0
- package/dist/settings-commands.test.js +549 -0
- package/dist/settings-create.test.d.ts +1 -0
- package/dist/settings-create.test.js +394 -0
- package/dist/settings-mounts.test.d.ts +1 -0
- package/dist/settings-mounts.test.js +467 -0
- package/dist/settings-paths.test.d.ts +1 -0
- package/dist/settings-paths.test.js +65 -0
- package/dist/settings-schema.d.ts +14 -0
- package/dist/settings-schema.js +14 -0
- package/dist/settings-workspaces.test.d.ts +1 -0
- package/dist/settings-workspaces.test.js +265 -0
- package/dist/spill-store.d.ts +17 -0
- package/dist/spill-store.js +54 -0
- package/dist/spill-store.test.d.ts +1 -0
- package/dist/spill-store.test.js +59 -0
- package/dist/subprocess.d.ts +9 -0
- package/dist/subprocess.js +369 -0
- package/dist/subprocess.test.d.ts +1 -0
- package/dist/subprocess.test.js +234 -0
- package/dist/test-support.d.ts +328 -0
- package/dist/test-support.js +437 -0
- package/dist/tool-defs.d.ts +26 -0
- package/dist/tool-defs.js +188 -0
- package/dist/tool-handlers.d.ts +2 -0
- package/dist/tool-handlers.js +546 -0
- package/dist/tool-params.d.ts +803 -0
- package/dist/tool-params.js +494 -0
- package/dist/tool-schemas.d.ts +2 -0
- package/dist/tool-schemas.js +5 -0
- package/dist/tool-views.d.ts +2 -0
- package/dist/tool-views.js +56 -0
- package/dist/views.test.d.ts +1 -0
- package/dist/views.test.js +23 -0
- package/dist/volumes.test.d.ts +1 -0
- package/dist/volumes.test.js +40 -0
- package/dist/workspace-binding.d.ts +40 -0
- package/dist/workspace-binding.js +275 -0
- package/dist/workspace-binding.test.d.ts +1 -0
- package/dist/workspace-binding.test.js +337 -0
- package/docs/architecture.md +173 -0
- package/docs/architecture.zh.md +129 -0
- package/docs/configuration.md +160 -0
- package/docs/configuration.zh.md +147 -0
- package/docs/development.md +142 -0
- package/docs/development.zh.md +135 -0
- package/docs/install-dsh-and-dsh-podman.md +165 -0
- package/docs/install-dsh-and-dsh-podman.zh.md +145 -0
- package/docs/usage.md +393 -0
- package/docs/usage.zh.md +315 -0
- package/package.json +119 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
import { createSnapshotStore } from "@deepseek-ai/dsh-client-store";
|
|
5
|
+
import { createCardClient } from "./card-client.js";
|
|
6
|
+
export const CONTAINER_NS = "podman";
|
|
7
|
+
const EMPTY_SNAPSHOT = {
|
|
8
|
+
version: "",
|
|
9
|
+
commit: "",
|
|
10
|
+
projectsRoot: "",
|
|
11
|
+
workspaces: [],
|
|
12
|
+
containers: [],
|
|
13
|
+
images: [],
|
|
14
|
+
volumes: [],
|
|
15
|
+
secrets: [],
|
|
16
|
+
caches: [],
|
|
17
|
+
};
|
|
18
|
+
// The card reads its live state from the host route and its preferences from
|
|
19
|
+
// the settings namespace. A command is one request, so nothing is re-delivered
|
|
20
|
+
// and no document round-trip is involved.
|
|
21
|
+
export class ContainerCardController {
|
|
22
|
+
scope;
|
|
23
|
+
store;
|
|
24
|
+
drafts = new Map();
|
|
25
|
+
client;
|
|
26
|
+
snapshot = EMPTY_SNAPSHOT;
|
|
27
|
+
busy = false;
|
|
28
|
+
notice = "";
|
|
29
|
+
directoryPicker;
|
|
30
|
+
constructor(scope, client = createCardClient()) {
|
|
31
|
+
this.scope = scope;
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.store = createSnapshotStore(this.project());
|
|
34
|
+
scope.subscribe(() => {
|
|
35
|
+
this.publish();
|
|
36
|
+
});
|
|
37
|
+
void this.reload();
|
|
38
|
+
}
|
|
39
|
+
draft(field, current) {
|
|
40
|
+
return this.drafts.get(field) ?? current;
|
|
41
|
+
}
|
|
42
|
+
project() {
|
|
43
|
+
const snapshot = this.scope.getSnapshot();
|
|
44
|
+
const value = snapshot.value;
|
|
45
|
+
return {
|
|
46
|
+
available: snapshot.status === "ready" && value !== undefined,
|
|
47
|
+
writable: snapshot.writable,
|
|
48
|
+
busy: this.busy,
|
|
49
|
+
notice: this.notice,
|
|
50
|
+
version: this.snapshot.version,
|
|
51
|
+
commit: this.snapshot.commit,
|
|
52
|
+
defaultImage: value?.defaultImage ?? "",
|
|
53
|
+
defaultImageDraft: this.draft("defaultImage", value?.defaultImage ?? ""),
|
|
54
|
+
socketsRoot: value?.socketsRoot ?? "",
|
|
55
|
+
socketsRootDraft: this.draft("socketsRoot", value?.socketsRoot ?? ""),
|
|
56
|
+
projectsRoot: this.snapshot.projectsRoot,
|
|
57
|
+
...(this.directoryPicker === undefined
|
|
58
|
+
? {}
|
|
59
|
+
: { directoryPicker: this.directoryPicker }),
|
|
60
|
+
workspaces: this.snapshot.workspaces,
|
|
61
|
+
containers: this.snapshot.containers,
|
|
62
|
+
images: this.snapshot.images,
|
|
63
|
+
volumes: this.snapshot.volumes,
|
|
64
|
+
secrets: this.snapshot.secrets,
|
|
65
|
+
caches: this.snapshot.caches,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
publish() {
|
|
69
|
+
this.store.set(this.project());
|
|
70
|
+
}
|
|
71
|
+
// setDirectoryPicker records the harness's directory picker (or clears it)
|
|
72
|
+
// and republishes, so the card shows or hides the browse affordance.
|
|
73
|
+
setDirectoryPicker(picker) {
|
|
74
|
+
this.directoryPicker = picker;
|
|
75
|
+
this.publish();
|
|
76
|
+
}
|
|
77
|
+
command(op, workspace, image, extra = {}) {
|
|
78
|
+
void this.dispatch({
|
|
79
|
+
op,
|
|
80
|
+
workspace,
|
|
81
|
+
image,
|
|
82
|
+
projectName: extra.projectName ?? "",
|
|
83
|
+
mounts: extra.mounts ?? [],
|
|
84
|
+
paths: extra.paths ?? [],
|
|
85
|
+
value: extra.value ?? "",
|
|
86
|
+
env: extra.env ?? {},
|
|
87
|
+
container: extra.container ?? "",
|
|
88
|
+
secret: extra.secret ?? "",
|
|
89
|
+
secretEnv: extra.secretEnv ?? "",
|
|
90
|
+
length: extra.length ?? 0,
|
|
91
|
+
charset: extra.charset ?? "",
|
|
92
|
+
packages: extra.packages ?? [],
|
|
93
|
+
secretEnvMap: extra.secretEnvMap ?? {},
|
|
94
|
+
cacheMode: extra.cacheMode ?? "",
|
|
95
|
+
mount: extra.mount ?? null,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async dispatch(request) {
|
|
99
|
+
this.busy = true;
|
|
100
|
+
this.notice = "";
|
|
101
|
+
this.publish();
|
|
102
|
+
try {
|
|
103
|
+
const result = await this.client.command(request);
|
|
104
|
+
this.notice = result.notice ?? "";
|
|
105
|
+
this.snapshot = await this.client.snapshot();
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
this.notice = error instanceof Error ? error.message : String(error);
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
this.busy = false;
|
|
112
|
+
this.publish();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async reload() {
|
|
116
|
+
try {
|
|
117
|
+
this.snapshot = await this.client.snapshot();
|
|
118
|
+
// A refresh clears the previous action's notice, so reopening the
|
|
119
|
+
// settings does not replay feedback from an earlier session.
|
|
120
|
+
this.notice = "";
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.notice = error instanceof Error ? error.message : String(error);
|
|
124
|
+
}
|
|
125
|
+
this.publish();
|
|
126
|
+
}
|
|
127
|
+
edit(field, text) {
|
|
128
|
+
this.drafts.set(field, text);
|
|
129
|
+
this.publish();
|
|
130
|
+
}
|
|
131
|
+
save(field) {
|
|
132
|
+
const draft = this.drafts.get(field);
|
|
133
|
+
if (draft === undefined)
|
|
134
|
+
return;
|
|
135
|
+
void this.scope.set(field, draft);
|
|
136
|
+
}
|
|
137
|
+
discard(field) {
|
|
138
|
+
this.drafts.delete(field);
|
|
139
|
+
this.publish();
|
|
140
|
+
}
|
|
141
|
+
inject() {
|
|
142
|
+
return {
|
|
143
|
+
hooks: { containerCard: this.store },
|
|
144
|
+
reload: () => {
|
|
145
|
+
void this.reload();
|
|
146
|
+
},
|
|
147
|
+
remove: (workspace) => this.command("remove", workspace, ""),
|
|
148
|
+
cleanCaches: (mode) => this.command("cache_clean", "", "", { cacheMode: mode }),
|
|
149
|
+
removeWorkspace: (workspace) => this.command("workspace_remove", workspace, ""),
|
|
150
|
+
recreate: (workspace, image, env) => this.command("recreate", workspace, image, { ...(env ? { env } : {}) }),
|
|
151
|
+
createContainer: (workspace, config) => this.command("create", workspace.workspaceSlug, config?.image ?? (workspace.imageId || this.defaultImage() || ""), {
|
|
152
|
+
projectName: workspace.projectName,
|
|
153
|
+
...(config?.mounts && config.mounts.length > 0 ? { mounts: config.mounts } : {}),
|
|
154
|
+
...(config?.env && Object.keys(config.env).length > 0 ? { env: config.env } : {}),
|
|
155
|
+
...(config?.paths && config.paths.length > 0 ? { paths: config.paths } : {}),
|
|
156
|
+
...(config?.secretEnv && Object.keys(config.secretEnv).length > 0 ? { secretEnvMap: config.secretEnv } : {}),
|
|
157
|
+
}),
|
|
158
|
+
startContainer: (workspace, container, config) => this.command("create", workspace.workspaceSlug, config?.image ?? (workspace.imageId || this.defaultImage() || ""), {
|
|
159
|
+
container,
|
|
160
|
+
...(config?.mounts && config.mounts.length > 0 ? { mounts: config.mounts } : {}),
|
|
161
|
+
...(config?.env && Object.keys(config.env).length > 0 ? { env: config.env } : {}),
|
|
162
|
+
...(config?.paths && config.paths.length > 0 ? { paths: config.paths } : {}),
|
|
163
|
+
...(config?.secretEnv && Object.keys(config.secretEnv).length > 0 ? { secretEnvMap: config.secretEnv } : {}),
|
|
164
|
+
}),
|
|
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
|
+
setContainerPaths: (workspace, container, paths) => this.command("container_path_set", workspace, "", { container, paths }),
|
|
169
|
+
createVolume: (name) => this.command("volume_create", name, ""),
|
|
170
|
+
removeVolume: (name) => this.command("volume_remove", name, ""),
|
|
171
|
+
removeImage: (imageId) => this.command("image_remove", imageId, ""),
|
|
172
|
+
rebuildImage: (imageId) => this.command("image_rebuild", imageId, ""),
|
|
173
|
+
rebuildAllImages: () => this.command("image_rebuild_all", "", ""),
|
|
174
|
+
buildImage: (imageId, parent, packages) => this.command("image_build", imageId, parent, { packages }),
|
|
175
|
+
rebuildBaseImage: (name) => this.command("image_base_rebuild", name, ""),
|
|
176
|
+
pullBaseImage: (name) => this.command("image_base_pull", name, ""),
|
|
177
|
+
setDefaultImage: (name) => {
|
|
178
|
+
this.edit("defaultImage", name);
|
|
179
|
+
this.save("defaultImage");
|
|
180
|
+
},
|
|
181
|
+
createSecret: (name, length, charset) => this.command("secret_create", name, "", {
|
|
182
|
+
...(length ? { length } : {}),
|
|
183
|
+
...(charset ? { charset } : {}),
|
|
184
|
+
}),
|
|
185
|
+
removeSecret: (name) => this.command("secret_remove", name, ""),
|
|
186
|
+
setSecret: (name, value) => this.command("secret_set", name, "", { value }),
|
|
187
|
+
addContainerSecret: (workspace, envVar, secret) => this.command("container_secret_add", workspace, "", {
|
|
188
|
+
container: "default",
|
|
189
|
+
secretEnv: envVar,
|
|
190
|
+
secret,
|
|
191
|
+
}),
|
|
192
|
+
removeContainerSecret: (workspace, envVar) => this.command("container_secret_remove", workspace, "", {
|
|
193
|
+
container: "default",
|
|
194
|
+
secretEnv: envVar,
|
|
195
|
+
}),
|
|
196
|
+
editDefaultImage: (text) => this.edit("defaultImage", text),
|
|
197
|
+
saveDefaultImage: () => this.save("defaultImage"),
|
|
198
|
+
discardDefaultImage: () => this.discard("defaultImage"),
|
|
199
|
+
editSocketsRoot: (text) => this.edit("socketsRoot", text),
|
|
200
|
+
saveSocketsRoot: () => this.save("socketsRoot"),
|
|
201
|
+
discardSocketsRoot: () => this.discard("socketsRoot"),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
defaultImage() {
|
|
205
|
+
return this.scope.getSnapshot().value?.defaultImage ?? "";
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ContainerCreateConfig, type ImageView, type WorkspaceView } from "./container-card-controller.js";
|
|
2
|
+
import { type DirectoryPickerFace } from "./directory-picker.js";
|
|
3
|
+
import { type ContainerPluginKey } from "./locales.js";
|
|
4
|
+
import { type ReactNode } from "react";
|
|
5
|
+
export declare function CreateContainerModal(props: {
|
|
6
|
+
t: (key: ContainerPluginKey) => string;
|
|
7
|
+
workspace: WorkspaceView;
|
|
8
|
+
images: readonly ImageView[];
|
|
9
|
+
volumes: readonly {
|
|
10
|
+
name: string;
|
|
11
|
+
}[];
|
|
12
|
+
secrets: readonly {
|
|
13
|
+
name: string;
|
|
14
|
+
}[];
|
|
15
|
+
busy: boolean;
|
|
16
|
+
defaultImage: string;
|
|
17
|
+
projectsRoot: string;
|
|
18
|
+
directoryPicker?: DirectoryPickerFace;
|
|
19
|
+
named: boolean;
|
|
20
|
+
existing: readonly string[];
|
|
21
|
+
open: boolean;
|
|
22
|
+
onClose: () => void;
|
|
23
|
+
onCreate: (container: string, config: ContainerCreateConfig) => void;
|
|
24
|
+
}): ReactNode;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EnvEditor, MountsEditor } from "./container-card-editors.js";
|
|
3
|
+
import { PathsList } from "./container-card-paths.js";
|
|
4
|
+
import { Field, namePattern, sanitizeName } from "./container-card-shared.js";
|
|
5
|
+
import { greyId, hint, imageSelect, sectionTitle } from "./container-card-styles.js";
|
|
6
|
+
import { Button, Input, Modal } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
7
|
+
import { useEffect, useId, useState } from "react";
|
|
8
|
+
export function CreateContainerModal(props) {
|
|
9
|
+
const { t, workspace, images, volumes, secrets, busy, defaultImage, projectsRoot, directoryPicker, named, existing, open, onClose, onCreate, } = props;
|
|
10
|
+
const [image, setImage] = useState(workspace.imageId || defaultImage);
|
|
11
|
+
const [name, setName] = useState("");
|
|
12
|
+
const [env, setEnv] = useState({});
|
|
13
|
+
const [mounts, setMounts] = useState(() => [
|
|
14
|
+
{
|
|
15
|
+
kind: "project",
|
|
16
|
+
project: workspace.projectName,
|
|
17
|
+
destination: "",
|
|
18
|
+
mode: "read_write",
|
|
19
|
+
volume: "",
|
|
20
|
+
secret: "",
|
|
21
|
+
},
|
|
22
|
+
]);
|
|
23
|
+
const [secretEnv, setSecretEnv] = useState({});
|
|
24
|
+
const [paths, setPaths] = useState([]);
|
|
25
|
+
const [attachSecret, setAttachSecret] = useState("");
|
|
26
|
+
const [attachVar, setAttachVar] = useState("");
|
|
27
|
+
const [submitting, setSubmitting] = useState(false);
|
|
28
|
+
const imageLabel = useId();
|
|
29
|
+
const nameLabel = useId();
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!open)
|
|
32
|
+
return;
|
|
33
|
+
setImage(workspace.imageId || defaultImage);
|
|
34
|
+
setName("");
|
|
35
|
+
setEnv({});
|
|
36
|
+
setMounts([
|
|
37
|
+
{
|
|
38
|
+
kind: "project",
|
|
39
|
+
project: workspace.projectName,
|
|
40
|
+
destination: "",
|
|
41
|
+
mode: "read_write",
|
|
42
|
+
volume: "",
|
|
43
|
+
secret: "",
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
setSecretEnv({});
|
|
47
|
+
setPaths([]);
|
|
48
|
+
setAttachSecret("");
|
|
49
|
+
setAttachVar("");
|
|
50
|
+
setSubmitting(false);
|
|
51
|
+
}, [open, workspace, defaultImage]);
|
|
52
|
+
const baseImages = images.filter((image) => image.isBase);
|
|
53
|
+
const customImages = images.filter((image) => !image.isBase);
|
|
54
|
+
const candidates = [
|
|
55
|
+
...baseImages.map((image) => image.imageId),
|
|
56
|
+
...customImages.map((image) => image.imageId),
|
|
57
|
+
];
|
|
58
|
+
const nameTaken = existing.includes(name);
|
|
59
|
+
const validName = namePattern.test(name) && name !== "default" && !nameTaken;
|
|
60
|
+
const canCreate = !named || validName;
|
|
61
|
+
const submit = () => {
|
|
62
|
+
if (!canCreate || submitting)
|
|
63
|
+
return;
|
|
64
|
+
setSubmitting(true);
|
|
65
|
+
onCreate(named ? name : "", {
|
|
66
|
+
image,
|
|
67
|
+
env,
|
|
68
|
+
mounts,
|
|
69
|
+
paths: paths.map((entry) => entry.path).filter((path) => path !== ""),
|
|
70
|
+
secretEnv,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const attach = () => {
|
|
74
|
+
const envVar = attachVar.trim();
|
|
75
|
+
if (envVar === "" || attachSecret === "")
|
|
76
|
+
return;
|
|
77
|
+
setSecretEnv({ ...secretEnv, [envVar]: attachSecret });
|
|
78
|
+
setAttachVar("");
|
|
79
|
+
};
|
|
80
|
+
return (_jsx(Modal, { open: open, onClose: onClose, title: named ? t("addContainer") : t("createContainer"), closeLabel: t("cancel"), footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", size: "sm", onClick: onClose, children: t("cancel") }), _jsx(Button, { variant: "primary", size: "sm", disabled: !canCreate || submitting, onClick: submit, children: named ? t("addContainer") : t("createContainer") })] }), children: _jsxs("div", { style: {
|
|
81
|
+
display: "flex",
|
|
82
|
+
flexDirection: "column",
|
|
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 ? (_jsx("option", { value: "", children: t("none") })) : null, candidates.map((imageId) => (_jsx("option", { value: imageId, children: imageId }, imageId)))] }) }), named ? (_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 ? (_jsx("p", { style: { ...hint, margin: 0 }, children: nameTaken
|
|
85
|
+
? t("containerNameTaken")
|
|
86
|
+
: t("invalidContainerName") })) : null] })) : 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
|
+
display: "flex",
|
|
88
|
+
flexWrap: "wrap",
|
|
89
|
+
alignItems: "center",
|
|
90
|
+
gap: "8px",
|
|
91
|
+
}, children: [_jsxs("code", { style: {
|
|
92
|
+
...greyId,
|
|
93
|
+
flex: 1,
|
|
94
|
+
fontSize: "13px",
|
|
95
|
+
color: "var(--dsw-alias-label-primary)",
|
|
96
|
+
}, children: [envVar, "=", secretName] }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy, onClick: () => {
|
|
97
|
+
const next = { ...secretEnv };
|
|
98
|
+
delete next[envVar];
|
|
99
|
+
setSecretEnv(next);
|
|
100
|
+
}, children: t("remove") })] }, envVar))), _jsxs("div", { style: {
|
|
101
|
+
display: "flex",
|
|
102
|
+
flexWrap: "wrap",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
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 ? (_jsx("option", { value: "", children: t("none") })) : 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
|
+
if (event.key === "Enter")
|
|
107
|
+
attach();
|
|
108
|
+
}, style: { width: "200px" } }), _jsx(Button, { variant: "outline", size: "sm", disabled: busy || attachVar.trim() === "" || attachSecret === "", onClick: attach, children: t("attachSecret") })] })] }) }));
|
|
109
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Context as ClientContext } from "@deepseek-ai/cordis";
|
|
2
|
+
export declare const DIRECTORY_CLASS = "dsh-podman-directory";
|
|
3
|
+
export declare const DIRECTORY_STYLES = "\n.dsh-podman-directory.dsh-podman-directory {\n width: min(680px, 100%);\n height: min(500px, calc(100dvh - 32px));\n padding: 0;\n gap: 0;\n --dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);\n --dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);\n}\n.dsh-podman-directory-scope {\n display: contents;\n}\n.dsh-podman-directory-header {\n display: flex;\n flex-direction: column;\n flex: none;\n gap: 8px;\n padding: 16px 14px 8px 24px;\n border-bottom: 0.5px solid var(--dsw-alias-border-l3);\n}\n.dsh-podman-directory-title {\n margin: 0;\n min-height: 28px;\n font-size: 16px;\n line-height: 24px;\n font-weight: 510;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-podman-directory-crumbbar {\n display: flex;\n align-items: center;\n gap: 4px;\n box-sizing: border-box;\n min-height: 24px;\n margin-left: -9px;\n padding: 0 8px;\n border: 1px solid transparent;\n border-radius: 8px;\n}\n.dsh-podman-directory-crumbs {\n display: flex;\n align-items: center;\n gap: 4px;\n flex: 0 1 auto;\n min-width: 0;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.dsh-podman-directory-crumbseat {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex: none;\n min-width: 0;\n}\n.dsh-podman-directory-crumb {\n border: none;\n background: transparent;\n padding: 0;\n max-width: 160px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n.dsh-podman-directory-crumb:hover {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-podman-directory-crumb:disabled {\n cursor: default;\n color: var(--dsw-alias-label-caption);\n}\n.dsh-podman-directory-crumbchevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-podman-directory-content {\n display: flex;\n flex-direction: column;\n flex: 1 1 0;\n min-height: 0;\n position: relative;\n padding: 16px 16px 16px 24px;\n}\n.dsh-podman-directory-miller {\n display: flex;\n align-items: stretch;\n flex: 1 1 0;\n min-height: 0;\n gap: 12px;\n overflow-x: auto;\n scrollbar-width: none;\n}\n.dsh-podman-directory-column {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1 1 0;\n min-width: 256px;\n margin: 0;\n list-style: none;\n overflow-y: auto;\n padding-right: 8px;\n}\n.dsh-podman-directory-divider {\n flex: none;\n width: 0.5px;\n background: var(--dsw-alias-border-l3);\n}\n.dsh-podman-directory-row {\n width: 100%;\n display: flex;\n align-items: center;\n gap: 4px;\n height: 28px;\n flex: none;\n padding: 4px;\n border: none;\n border-radius: 6px;\n background: transparent;\n text-align: left;\n cursor: pointer;\n}\n.dsh-podman-directory-seat {\n display: flex;\n flex: none;\n}\n.dsh-podman-directory-row:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n.dsh-podman-directory-row-selected,\n.dsh-podman-directory-row-selected:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover));\n}\n.dsh-podman-directory-row:disabled {\n cursor: default;\n}\n.dsh-podman-directory-rowicon {\n flex: none;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-podman-directory-rowicon-selected {\n flex: none;\n color: var(--dsw-alias-button-info-fill);\n}\n.dsh-podman-directory-rowname {\n flex: 1 1 0;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-podman-directory-rowchevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-podman-directory-status,\n.dsh-podman-directory-error {\n margin: 0;\n padding: 4px;\n font-size: 12px;\n line-height: 18px;\n}\n.dsh-podman-directory-status {\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-podman-directory-error {\n color: var(--dsw-alias-state-error-primary);\n}\n.dsh-podman-directory-loading {\n position: absolute;\n right: 16px;\n bottom: 8px;\n padding: 2px 8px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n.dsh-podman-directory-footer {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n flex: none;\n padding: 16px 24px;\n border-top: 0.5px solid var(--dsw-alias-border-l3);\n}\n.dsh-podman-directory-toggle {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 0;\n border: none;\n background: transparent;\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n.dsh-podman-directory-toggle[aria-pressed=\"true\"] {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-podman-directory-toggle:disabled {\n cursor: default;\n color: var(--dsw-alias-label-caption);\n}\n.dsh-podman-directory-gap {\n flex: 1 1 0;\n}\n.dsh-podman-directory-action {\n min-width: 72px;\n}\n";
|
|
4
|
+
/** Inject the directory browser stylesheet for the owning plugin lifetime. */
|
|
5
|
+
export declare function installDirectoryStyles(ctx: ClientContext): void;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 Elouan Martinet <exa@elou.world>
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: MIT
|
|
4
|
+
// The class passed to Modal for the project directory browser. The layout,
|
|
5
|
+
// typography and tokens mirror dsh's own directory browser (680x500 dialog,
|
|
6
|
+
// two Miller columns, chevron breadcrumbs, 28px rows with folder icons), so the
|
|
7
|
+
// plugin's picker reads like the harness's workspace directory selection. The
|
|
8
|
+
// dialog is clamped to the projects root, so its breadcrumb trail starts there.
|
|
9
|
+
export const DIRECTORY_CLASS = "dsh-podman-directory";
|
|
10
|
+
export const DIRECTORY_STYLES = `
|
|
11
|
+
.dsh-podman-directory.dsh-podman-directory {
|
|
12
|
+
width: min(680px, 100%);
|
|
13
|
+
height: min(500px, calc(100dvh - 32px));
|
|
14
|
+
padding: 0;
|
|
15
|
+
gap: 0;
|
|
16
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
17
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
18
|
+
}
|
|
19
|
+
.dsh-podman-directory-scope {
|
|
20
|
+
display: contents;
|
|
21
|
+
}
|
|
22
|
+
.dsh-podman-directory-header {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
flex: none;
|
|
26
|
+
gap: 8px;
|
|
27
|
+
padding: 16px 14px 8px 24px;
|
|
28
|
+
border-bottom: 0.5px solid var(--dsw-alias-border-l3);
|
|
29
|
+
}
|
|
30
|
+
.dsh-podman-directory-title {
|
|
31
|
+
margin: 0;
|
|
32
|
+
min-height: 28px;
|
|
33
|
+
font-size: 16px;
|
|
34
|
+
line-height: 24px;
|
|
35
|
+
font-weight: 510;
|
|
36
|
+
color: var(--dsw-alias-label-primary);
|
|
37
|
+
}
|
|
38
|
+
.dsh-podman-directory-crumbbar {
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 4px;
|
|
42
|
+
box-sizing: border-box;
|
|
43
|
+
min-height: 24px;
|
|
44
|
+
margin-left: -9px;
|
|
45
|
+
padding: 0 8px;
|
|
46
|
+
border: 1px solid transparent;
|
|
47
|
+
border-radius: 8px;
|
|
48
|
+
}
|
|
49
|
+
.dsh-podman-directory-crumbs {
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: 4px;
|
|
53
|
+
flex: 0 1 auto;
|
|
54
|
+
min-width: 0;
|
|
55
|
+
overflow-x: auto;
|
|
56
|
+
scrollbar-width: none;
|
|
57
|
+
}
|
|
58
|
+
.dsh-podman-directory-crumbseat {
|
|
59
|
+
display: inline-flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 4px;
|
|
62
|
+
flex: none;
|
|
63
|
+
min-width: 0;
|
|
64
|
+
}
|
|
65
|
+
.dsh-podman-directory-crumb {
|
|
66
|
+
border: none;
|
|
67
|
+
background: transparent;
|
|
68
|
+
padding: 0;
|
|
69
|
+
max-width: 160px;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
text-overflow: ellipsis;
|
|
72
|
+
white-space: nowrap;
|
|
73
|
+
font-size: 13px;
|
|
74
|
+
line-height: 20px;
|
|
75
|
+
font-weight: 500;
|
|
76
|
+
color: var(--dsw-alias-label-tertiary);
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
}
|
|
79
|
+
.dsh-podman-directory-crumb:hover {
|
|
80
|
+
color: var(--dsw-alias-label-primary);
|
|
81
|
+
}
|
|
82
|
+
.dsh-podman-directory-crumb:disabled {
|
|
83
|
+
cursor: default;
|
|
84
|
+
color: var(--dsw-alias-label-caption);
|
|
85
|
+
}
|
|
86
|
+
.dsh-podman-directory-crumbchevron {
|
|
87
|
+
flex: none;
|
|
88
|
+
color: var(--dsw-alias-label-tertiary);
|
|
89
|
+
}
|
|
90
|
+
.dsh-podman-directory-content {
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
flex: 1 1 0;
|
|
94
|
+
min-height: 0;
|
|
95
|
+
position: relative;
|
|
96
|
+
padding: 16px 16px 16px 24px;
|
|
97
|
+
}
|
|
98
|
+
.dsh-podman-directory-miller {
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: stretch;
|
|
101
|
+
flex: 1 1 0;
|
|
102
|
+
min-height: 0;
|
|
103
|
+
gap: 12px;
|
|
104
|
+
overflow-x: auto;
|
|
105
|
+
scrollbar-width: none;
|
|
106
|
+
}
|
|
107
|
+
.dsh-podman-directory-column {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: 2px;
|
|
111
|
+
flex: 1 1 0;
|
|
112
|
+
min-width: 256px;
|
|
113
|
+
margin: 0;
|
|
114
|
+
list-style: none;
|
|
115
|
+
overflow-y: auto;
|
|
116
|
+
padding-right: 8px;
|
|
117
|
+
}
|
|
118
|
+
.dsh-podman-directory-divider {
|
|
119
|
+
flex: none;
|
|
120
|
+
width: 0.5px;
|
|
121
|
+
background: var(--dsw-alias-border-l3);
|
|
122
|
+
}
|
|
123
|
+
.dsh-podman-directory-row {
|
|
124
|
+
width: 100%;
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
gap: 4px;
|
|
128
|
+
height: 28px;
|
|
129
|
+
flex: none;
|
|
130
|
+
padding: 4px;
|
|
131
|
+
border: none;
|
|
132
|
+
border-radius: 6px;
|
|
133
|
+
background: transparent;
|
|
134
|
+
text-align: left;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
}
|
|
137
|
+
.dsh-podman-directory-seat {
|
|
138
|
+
display: flex;
|
|
139
|
+
flex: none;
|
|
140
|
+
}
|
|
141
|
+
.dsh-podman-directory-row:hover:not(:disabled) {
|
|
142
|
+
background: var(--dsw-alias-interactive-bg-hover);
|
|
143
|
+
}
|
|
144
|
+
.dsh-podman-directory-row-selected,
|
|
145
|
+
.dsh-podman-directory-row-selected:hover:not(:disabled) {
|
|
146
|
+
background: var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover));
|
|
147
|
+
}
|
|
148
|
+
.dsh-podman-directory-row:disabled {
|
|
149
|
+
cursor: default;
|
|
150
|
+
}
|
|
151
|
+
.dsh-podman-directory-rowicon {
|
|
152
|
+
flex: none;
|
|
153
|
+
color: var(--dsw-alias-label-secondary);
|
|
154
|
+
}
|
|
155
|
+
.dsh-podman-directory-rowicon-selected {
|
|
156
|
+
flex: none;
|
|
157
|
+
color: var(--dsw-alias-button-info-fill);
|
|
158
|
+
}
|
|
159
|
+
.dsh-podman-directory-rowname {
|
|
160
|
+
flex: 1 1 0;
|
|
161
|
+
min-width: 0;
|
|
162
|
+
overflow: hidden;
|
|
163
|
+
text-overflow: ellipsis;
|
|
164
|
+
white-space: nowrap;
|
|
165
|
+
font-size: 13px;
|
|
166
|
+
line-height: 20px;
|
|
167
|
+
font-weight: 500;
|
|
168
|
+
color: var(--dsw-alias-label-primary);
|
|
169
|
+
}
|
|
170
|
+
.dsh-podman-directory-rowchevron {
|
|
171
|
+
flex: none;
|
|
172
|
+
color: var(--dsw-alias-label-tertiary);
|
|
173
|
+
}
|
|
174
|
+
.dsh-podman-directory-status,
|
|
175
|
+
.dsh-podman-directory-error {
|
|
176
|
+
margin: 0;
|
|
177
|
+
padding: 4px;
|
|
178
|
+
font-size: 12px;
|
|
179
|
+
line-height: 18px;
|
|
180
|
+
}
|
|
181
|
+
.dsh-podman-directory-status {
|
|
182
|
+
color: var(--dsw-alias-label-secondary);
|
|
183
|
+
}
|
|
184
|
+
.dsh-podman-directory-error {
|
|
185
|
+
color: var(--dsw-alias-state-error-primary);
|
|
186
|
+
}
|
|
187
|
+
.dsh-podman-directory-loading {
|
|
188
|
+
position: absolute;
|
|
189
|
+
right: 16px;
|
|
190
|
+
bottom: 8px;
|
|
191
|
+
padding: 2px 8px;
|
|
192
|
+
font-size: 12px;
|
|
193
|
+
line-height: 18px;
|
|
194
|
+
color: var(--dsw-alias-label-secondary);
|
|
195
|
+
background: var(--dsw-alias-bg-layer-2);
|
|
196
|
+
}
|
|
197
|
+
.dsh-podman-directory-footer {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
flex-wrap: wrap;
|
|
201
|
+
gap: 8px;
|
|
202
|
+
flex: none;
|
|
203
|
+
padding: 16px 24px;
|
|
204
|
+
border-top: 0.5px solid var(--dsw-alias-border-l3);
|
|
205
|
+
}
|
|
206
|
+
.dsh-podman-directory-toggle {
|
|
207
|
+
display: inline-flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
gap: 4px;
|
|
210
|
+
padding: 0;
|
|
211
|
+
border: none;
|
|
212
|
+
background: transparent;
|
|
213
|
+
font-size: 13px;
|
|
214
|
+
line-height: 20px;
|
|
215
|
+
font-weight: 500;
|
|
216
|
+
color: var(--dsw-alias-label-secondary);
|
|
217
|
+
cursor: pointer;
|
|
218
|
+
}
|
|
219
|
+
.dsh-podman-directory-toggle[aria-pressed="true"] {
|
|
220
|
+
color: var(--dsw-alias-label-primary);
|
|
221
|
+
}
|
|
222
|
+
.dsh-podman-directory-toggle:disabled {
|
|
223
|
+
cursor: default;
|
|
224
|
+
color: var(--dsw-alias-label-caption);
|
|
225
|
+
}
|
|
226
|
+
.dsh-podman-directory-gap {
|
|
227
|
+
flex: 1 1 0;
|
|
228
|
+
}
|
|
229
|
+
.dsh-podman-directory-action {
|
|
230
|
+
min-width: 72px;
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
233
|
+
/** Inject the directory browser stylesheet for the owning plugin lifetime. */
|
|
234
|
+
export function installDirectoryStyles(ctx) {
|
|
235
|
+
if (typeof document === "undefined")
|
|
236
|
+
return;
|
|
237
|
+
ctx.effect(() => {
|
|
238
|
+
const tag = document.createElement("style");
|
|
239
|
+
tag.dataset.plugin = "@exagone313/dsh-podman";
|
|
240
|
+
tag.textContent = DIRECTORY_STYLES;
|
|
241
|
+
document.head.appendChild(tag);
|
|
242
|
+
return () => {
|
|
243
|
+
tag.remove();
|
|
244
|
+
};
|
|
245
|
+
}, "podman: directory styles");
|
|
246
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type DirectoryPickerFace } from "./directory-picker.js";
|
|
2
|
+
import { type ContainerPluginKey } from "./locales.js";
|
|
3
|
+
import { type ReactNode } from "react";
|
|
4
|
+
export declare function DirectoryPickerModal(props: {
|
|
5
|
+
t: (key: ContainerPluginKey) => string;
|
|
6
|
+
open: boolean;
|
|
7
|
+
root: string;
|
|
8
|
+
start: string;
|
|
9
|
+
picker: DirectoryPickerFace;
|
|
10
|
+
onSelect: (path: string) => void;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
}): ReactNode;
|