Conversation
…and rephrase section titles
…te UI accordingly
…ons object for parameters
…lection and custom intervals
…Test - Simplified IconTest by consolidating setup and teardown processes, removing unnecessary comments, and streamlining icon file handling. - Removed SettingsBackupTest, SettingsPageTest, and StackInfoSourceTest as they were deemed unnecessary. - Introduced ComposeListEndpointTest to validate the rendering of Docker Compose stacks and ensure proper handling of empty states.
- Created package.json and package-lock.json for e2e tests with Playwright dependencies. - Added Playwright configuration file (playwright.config.ts) for test setup. - Implemented script to create storage state for authentication (create-storage-state.mjs). - Developed comprehensive end-to-end tests for Compose Manager lifecycle and read-only checks. - Included TypeScript configuration for test files.
…lete notifications
…act shared helpers
…ith modal interactions and image validation
…nce image update accuracy
…hance WebSocket management for hidden tabs
…radio Replace the two adjacent External Compose Path and External Compose File inputs with a single Compose Source radio group with three options — Project folder (default), External folder, Specific compose file — and reveal only the picker input relevant to the selected mode. This makes the mutual exclusion structural. Switching modes clears the other input's value so no state is retained across choices, and consequently the save-time "Set either External Compose Path or External Compose File, not both" swal is removed. - ComposeManager.php: new radio group inside Compose Sources & Files. Each picker input is wrapped in a display-toggle div and reused by the existing setStackSettings save flow (same field IDs, same POST keys). A stable settings-external-compose-file-warning element is moved into the HTML so the inline "inside project folder" warning has a consistent location. - composeManagerMain.js: new setComposeSource() / normalizeComposeSourceMode() helpers wired to a delegated change handler on the radio group. loadSettingsData now derives the initial mode from response state (externalComposeFilePath -> file, externalComposePath -> folder, invalidIndirectPath -> preserve indirectMode, else project) and calls setComposeSource with change tracking suppressed. Reset and fail paths also normalize back to project mode. The still-valid "file is inside stack project folder" save-time swal is kept as belt-and-suspenders until slice 6 tightens inline validation across the modal.
Replace the ambiguous "Use Docker Compose default file discovery (no explicit -f flags)" checkbox with a computed discovery-mode badge and a single explicit toggle button. The prior UI silently unchecked itself when Env File Path, External Compose File, or Additional Compose Files were set — hiding what happened behind a warning banner. The new UI shows current mode as a badge at all times and shows a Lock reason when overrides are active. - Rename the extra-compose disclosure summary from "External files (advanced)" to "Absolute paths (outside compose source folder)" — the word "external" is now used consistently for source-mode picking only. - ComposeManager.php: replace visible checkbox+label with a badge (#settings-discovery-mode-badge) and a toggle button (#settings-discovery-mode-toggle). Keep #settings-use-default-compose-files in the DOM as a hidden source of truth so the existing load/save round-trip (originalSettings['use-default-compose-files'], the setStackSettings POST field) works unchanged. - composeManagerMain.js: updateSettingsDefaultComposeDiscoveryState() now updates the badge label, shows/hides the toggle button, and renders the lock reason instead of silently forcing the checkbox off with only a warning banner. Add a delegated click handler on the toggle that flips the hidden checkbox and re-renders. When overrides lock discovery to explicit, the toggle is hidden and the reason line is shown.
Adds an Effective Command readout at the bottom of the per-stack Compose Sources & Files section that mirrors the exact docker compose invocation the plugin will use (project directory, -f file flags, --env-file, --profile flags, project name). - Exec.php: new previewComposeArgs AJAX action wraps StackInfo::buildComposeArgs() and getDefaultProfiles(). Returns filePaths, envFilePath, profiles, projectName, projectDirectory, and useDefaultFileDiscovery. - ComposeManager.php: new settings-field with a monospace <pre> block plus a discreet "Unsaved changes — apply to refresh." pill. - composeManagerMain.js: loadEffectiveCommandPreview() fetches from the new endpoint; renderEffectiveCommandPreview() formats the response as backslash-continued shell lines. Wired to run on loadSettingsData success and after each successful save. updateEffectiveCommandDirtyIndicator() toggles the pill and is called from every settings mutation path (generic input tracker, extra-compose files handler, override management change handler). Fail/reset paths clear the readout. The preview reflects the saved state only — precisely matching the authoritative buildComposeArgs() output — with a visible dirty indicator whenever unsaved changes could shift it.
Replaces the three save-time swals in saveSettings with inline
per-field errors that gate the Apply button. Users see problems
where they typed them and no longer submit only to be bounced back
by a modal error.
- New helpers setSettingsFieldError() / clearSettingsFieldError()
and clearAllSettingsValidationErrors() manage a per-field error
slot (id="settings-<fieldId>-error") and a
editorModal.settingsValidationErrors Set. updateSaveButtonState()
now disables Apply when the error Set is non-empty.
- WebUI URL: new inline handler validates on input and reports
"Invalid URL" or "Bare [PORT] not supported" via the error slot.
- External Compose File: the existing "inside project folder"
warning is promoted from an ad-hoc dynamic element to the shared
error system so it also blocks Apply.
- ComposeManager.php: static error slots #settings-webui-url-error
and #settings-external-compose-file-error. Old dynamic
-warning element removed; setComposeSource() now calls
clearSettingsFieldError('external-compose-file') on mode change.
- Errors are cleared on modal open, close, and stack switch, so
state from a previous stack doesn't leak forward.
- saveSettings drops the three matching swals; only inline
validation remains as the gate.
Extract the Compose Sources & Files section from the Settings tab
into a dedicated Sources tab placed just before Settings in the tab
bar. Runtime Defaults and Labels & Overrides stay on the Settings
tab.
- ComposeManager.php: new #editor-tab-sources button (Files icon)
inserted between Labels and Settings, and a new #editor-panel-sources
panel with the extracted section wrapped in a .settings-panel so the
same scrolling/layout styles apply. Section removed from the Settings
panel.
- composeManagerMain.js:
* switchTab: add 'sources' to validTabs; redirect blocked switches
to 'sources' instead of 'settings' when path-sensitive changes
are pending (that's where those fields now live).
* Arrow-key tab navigation array updated.
* getEditorSaveTargetPath / getEditorActiveFilePath treat 'sources'
the same as 'settings' (no active file, saves via stackMeta).
* updateTabModifiedState splits editorModal.modifiedSettings by
field ownership via a new SOURCES_TAB_SETTING_FIELDS set so the
modified indicator lights up the correct tab.
* saveCurrentTab guard now allows saving from the Sources tab
when path-sensitive fields are dirty (previously only Settings).
* enforcePathSettingsExclusivity swal wording updated to reference
Sources instead of Settings.
Field IDs, POST actions, saved metadata, and validation flow are
unchanged; users on existing stacks see the same behavior with the
new tab layout.
Rebuild the Add New Compose Stack modal to use the same
.settings-section / .settings-field layout as the editor's Sources
tab. Purely visual + naming; behavior is unchanged.
- Group fields into Stack Identity, Compose Sources & Files, and
Labels & Overrides sections with icon-prefixed titles.
- Rename "Indirect Path" / "Indirect Compose File" to
"External Compose Path" / "External Compose File" for parity
with the editor. DOM IDs renamed accordingly
(compose-stack-indirect{,-file} → compose-stack-external-{path,file}),
event bindings and the discovery-state helper updated. Backend POST
fields (stackPath, stackFilePath) unchanged.
- Modal error message about mutual exclusion now says External Compose
Path / File.
- Rewrite Override File Management help copy to spell out the Labels
tab side effect (form vs raw YAML editor).
- Widen modal from 560px to 680px and drop the accordion + invalid
</br> so all options are visible at once.
- Move the shared error banner above the sections.
Merge External Compose Path + External Compose File into a single Compose Source radio (Project folder / External folder / Specific compose file) with progressive disclosure — same pattern the editor's Sources tab uses. Kills the "both set" submit-time modal error since the two states are now mutually exclusive by construction. - Refactor setComposeSource into setComposeSourceForScope(scope, ...) driven by COMPOSE_SOURCE_SCOPE_CONFIGS, with entries for 'settings' (editor) and 'add-stack'. Legacy editor call sites keep working via a thin setComposeSource wrapper. - Add-stack modal: replace the two adjacent input fields with a radio group; each picker input lives in its own display-toggle wrap. On modal init, bind a change.addStackComposeSource handler and initialize to 'project' with change tracking suppressed. - Remove the submit-time "Set either External Compose Path or External Compose File, not both" error (structurally unreachable now). Backend check in Exec.php stays as defense in depth. POST fields (stackPath, stackFilePath) and server-side handling are unchanged.
Replace the plain "Use Docker Compose default file discovery" checkbox in the Add New Compose Stack modal with the same status badge + explicit toggle button the editor's Sources tab now uses. When External Compose File or ENV path is set, the toggle hides and a lock-reason line explains why discovery is forced to explicit -f flags. - Extract updateSettingsDefaultComposeDiscoveryState and updateAddStackDefaultComposeDiscoveryState into a shared updateDiscoveryModeUI(scope, suppressChangeTracking) driven by DISCOVERY_MODE_SCOPE_CONFIGS. Both call sites become one-line delegates that select their scope. The scope config carries the target DOM IDs and a per-scope getManualOverrideReasons() so add-stack (no Additional Compose Files field) and editor share the same rendering logic. - Add badge (#compose-stack-discovery-mode-badge) and toggle button (#compose-stack-discovery-mode-toggle) to the add-stack modal; keep #compose-stack-use-default-compose-files in the DOM as a hidden source of truth so the submit payload and the getConfig() hydration path are unchanged. - Wire a click.addStackDiscovery handler that flips the hidden checkbox and re-renders. Initial render runs on modal open.
Replace the single "Automatic (disable for Manual/raw override mode)" checkbox with an explicit Automatic/Manual radio pair whose labels spell out the Labels-tab side effect (form editor vs raw YAML editor) that the checkbox was hiding. - Modal template: two radio inputs named compose-stack-override-management with values 'automatic' (default) and 'manual', each carrying the matching help copy inline. - getConfig() hydration path now maps NEW_STACK_OVERRIDE_MANAGEMENT_AUTOMATIC to the selected radio value instead of a checkbox state. - submitComposeStackModal reads the checked radio and converts to the existing overrideManagementAutomatic 'true'/'false' POST field so the server contract is unchanged.
Move stack-name and external-path validation from submit-time to
live inline feedback. The Create button is now disabled until the
form is valid.
- Add per-field error slots #compose-stack-name-error,
#compose-stack-external-path-error, #compose-stack-external-file-error
wired via updateAddStackValidity(), which runs on input/change of
name, external path/file, and the Compose Source radio group.
- Rules enforced live:
* Stack name required (previously only surfaced after clicking Create).
* When Compose Source is External folder: path required and must
be under /mnt/ or /boot/config/.
* When Compose Source is Specific compose file: path required,
must be under /mnt/ or /boot/config/, and must end in
.yml/.yaml. Mirrors the server-side checks in Exec.php:addStack.
- Give the Create button id #compose-stack-create-btn and start it
disabled; toggle disabled based on validation state.
- Add a Project folder slug preview under Stack Name, computed in JS
by composeSanitizeProjectSlug() which mirrors
compose_manager_sanitize_project_name (ProjectNameSanitizer.php)
so users see the folder name that will be created.
refactor(Exec): remove null coalescing for project name and directory refactor(pkg_build): enhance checksum handling in download function
- compose_fetch_icon_to_cache(): download/read icon source, normalize to PNG via GD when available, write to plugin-owned cache dir; SSRF-safe for http(s) - compose_seed_docker_manager_icon(): copy cached PNG to both DM cache paths - compose_icon_to_png_bytes(): GD raster → PNG; raw pass-through when GD absent - compose_icon_is_safe_host(): block private/loopback hosts on remote fetches - compose_icon_ext_to_mime(): file-extension → MIME hint
#141) Call compose_fetch_icon_to_cache + compose_seed_docker_manager_icon after every composeResolveContainerIcon hit in getStackContainers and checkAllStacksUpdates. fetch is a no-op on cache hit; seed skips containers already present in DM cache.
After clearing DM cache files, also unlink the plugin's own cached PNG (keyed by icon URL hash) so the next container-icon resolve re-fetches a fresh copy.
…mp path (#141) Tests cover: - compose_fetch_icon_to_cache: data URI, cache hit, force refresh, SVG rejection, path traversal/out-of-allowlist blocking, unsupported schemes - compose_icon_is_safe_host: loopback + private-range rejection - compose_icon_ext_to_mime: all known extensions + unknown fallback - compose_icon_to_png_bytes: raw PNG pass-through (GD absent), SVG null, garbage null - compose_seed_docker_manager_icon: empty path and invalid name no-ops GD-dependent cases are guarded with markTestSkipped so CI passes cleanly.
…CACHE_DIR constant
- versions.env: pin resvg v0.48.1 + SHA256 alongside COMPOSE_VERSION - build.sh / build.ps1: parse RESVG_VERSION + RESVG_SHA256, pass as env vars - pkg_build.sh: download resvg-linux-x86_64.tar.gz (pinned SHA), install to /usr/local/emhttp/plugins/compose.manager/bin/resvg; include in release_info - Defines.php: add COMPOSE_RESVG_BIN constant - Util.php: compose_icon_to_png_bytes() tries resvg (stdin→tempfile) for SVG; GD path unchanged for raster; resvg not present = null (graceful skip) - tests.yml: add gd to PHP extensions so GD-gated tests run in CI - bootstrap.php: pre-define COMPOSE_RESVG_BIN pointing to /tmp/resvg when dev binary is present, falling back to plugin install path - IconCacheTest: split SVG test into resvg-present / resvg-absent variants; add testFetchSvgDataUriSucceedsWithResvg integration test
Log at debug level under 'icon-cache' category: - cache hit (skipped download) - successful cache write (source, path, byte size) - conversion failure (source, mime hint) - Docker Manager seed per target path - plugin cache entry cleared on clearIconCache
…Ls (#141) Add IconCache.php endpoint: accepts ?src=<url>, calls compose_fetch_icon_to_cache, serves cached PNG with 24h browser cache; falls back to 302 redirect on cache miss. Add compose_icon_browser_url() PHP helper: wraps http(s) sources in proxy URL, passes data: and local paths through unchanged. Add composeIconSrc() JS helper: same logic client-side, replaces isValidIconSrc +ternary pattern at every icon rendering site. Wire into: ComposeList.php (server-side img src), composeManagerMain.js (container list, update dialog, labels editor, deleted-service preview), compose.manager.dashboard.page (container row and stack tile icons).
…ear Icon Cache button (#141) - imagealphablending(false) + imagesavealpha(true) before imagepng() preserves the alpha channel — without these GD composites onto white, breaking transparency - Add clearAllIconCache Exec action: deletes all *.png from COMPOSE_ICON_CACHE_DIR - Add 'Clear Icon Cache' button to Settings page (alongside Clear Update Cache), with swal confirm + count of files removed in success message
- Add compose_fetch_icon_to_cache(): download/read icon from HTTP URL, data URI, or allowed local path; normalize to PNG; persist to plugin cache dir - Add compose_icon_to_png_bytes(): GD-based raster conversion with alpha preservation; SVG path tries bundled resvg then falls back to rsvg-convert - Add compose_seed_docker_manager_icon(): copies cached PNG into both Docker Manager RAM and USB image cache directories so the Docker tab shows the correct icon for compose-managed containers - Wire compose_fetch_icon_to_cache + compose_seed_docker_manager_icon into composeResolveContainerIcon (already in place in Exec.php) - Add helper compose_icon_ext_to_mime() and SSRF-safe compose_icon_is_safe_host() - Add regression tests: PNG data URI caching, idempotency, rejected paths, local file caching, SVG graceful-fail, Docker Manager seeding
…lify tests by removing GD dependency checks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR merges
devintomainwith a reliability-focused update across stack operations, update detection, settings UX, and test coverage.The goal is to reduce stale or false states, make automation safer, clarify operator controls, and increase confidence through broader regression coverage.
Changes
1. Stack lifecycle reliability
2. Update detection and runtime behavior
3. Settings page modernization
4. Test strategy expansion
Bug Fixes
livemode — exhausts nginx nchan shared memory in ~70s #130: preventscompose_infofrom publishing full payloads per stats line in live mode, avoiding nchan shared-memory exhaustion.up -d: the digest check reads container images, whichpullcan't change #132: corrects the auto-update digest comparison and skipsup -dwhen the stack is not running.compose.shswallows the real exit code — autostart silently logs "success" for stacks that failed to start #135: preserves the real compose command exit status instead of reporting a false success.Scope
devnot inmainValidation