mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
feat(docker): make the container field a picker you can also type into
Typing a container name from memory is error-prone. The field becomes a native datalist: pick from the engine's containers, type to filter, or type a name that is not listed (the engine may be remote, or the container may not exist yet). A datalist gives all three natively, so no dropdown state machine is introduced. Adds listDockerContainers and GET /api/docker-hosts/:hostId/containers, following the listRemoteCodemanSessions discovery precedent: read-only and never throwing, so an unreachable daemon returns an empty list and the field degrades to plain text instead of erroring. Stopped containers stay in the list, sorted after running ones and labelled. Attaching does require a running container, but hiding stopped ones turns "my container is not in the list" into a dead end, while showing `Exited (137) 8 days ago` says exactly what to fix.
This commit is contained in:
@@ -2343,6 +2343,18 @@ Object.assign(CodemanApp.prototype, {
|
||||
if (adoptCheck) adoptCheck.onclick = () => this._dockerAdoptPreflight();
|
||||
const adoptJump = document.getElementById('dockerAdoptJumpBtn');
|
||||
if (adoptJump) adoptJump.onclick = () => this.jumpToDockerAdopt();
|
||||
// Containers come from the host profile, so switching Host ID invalidates the
|
||||
// suggestions. Dropping the marker (rather than refetching here) keeps the
|
||||
// fetch lazy — it happens when adopt mode is actually on.
|
||||
const hostIdInput = document.getElementById('dockerHostId');
|
||||
if (hostIdInput) {
|
||||
hostIdInput.onchange = () => {
|
||||
delete document.getElementById('dockerContainerList')?.dataset.loadedFor;
|
||||
if (document.getElementById('dockerAdoptExisting')?.checked) void this._loadDockerContainerOptions();
|
||||
};
|
||||
}
|
||||
// A fresh open re-reads the engine: containers start and stop between visits.
|
||||
delete document.getElementById('dockerContainerList')?.dataset.loadedFor;
|
||||
this._syncDockerAdoptMode();
|
||||
// Scroll-into-view on focus for mobile keyboard visibility
|
||||
modal.querySelectorAll('input[type="text"]').forEach(input => {
|
||||
@@ -2913,6 +2925,34 @@ Object.assign(CodemanApp.prototype, {
|
||||
const adopting = document.getElementById('dockerAdoptExisting')?.checked;
|
||||
if (adopting) modal.setAttribute('data-docker-adopt', '1');
|
||||
else modal.removeAttribute('data-docker-adopt');
|
||||
if (adopting) void this._loadDockerContainerOptions();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fill the container-name `<datalist>`. A native datalist is deliberate: the
|
||||
* field must accept a free-typed name (the engine may be remote, or the
|
||||
* container may not exist yet when the form is filled), and datalist gives
|
||||
* type-to-filter over the suggestions without a custom dropdown.
|
||||
*
|
||||
* Best-effort by design — the endpoint returns [] for an unreachable daemon,
|
||||
* and an empty list simply leaves the field as plain text input.
|
||||
*/
|
||||
async _loadDockerContainerOptions() {
|
||||
const list = document.getElementById('dockerContainerList');
|
||||
if (!list) return;
|
||||
const hostId = document.getElementById('dockerHostId')?.value.trim() || 'local';
|
||||
if (list.dataset.loadedFor === hostId) return; // one fetch per host per open
|
||||
const data = await this._apiJson(`/api/docker-hosts/${encodeURIComponent(hostId)}/containers`);
|
||||
const containers = data?.containers || [];
|
||||
list.textContent = '';
|
||||
for (const c of containers) {
|
||||
const option = document.createElement('option');
|
||||
option.value = c.name;
|
||||
// Engine-supplied strings: set as text, never as markup.
|
||||
option.textContent = c.running ? `${c.image} · ${c.status}` : `${c.image} · ${c.status} (not running)`;
|
||||
list.appendChild(option);
|
||||
}
|
||||
list.dataset.loadedFor = hostId;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user