diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index cc966d37..f80f7c33 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -8,6 +8,8 @@ body:
value: |
Thanks for reporting this. Please include only the details needed to reproduce the issue quickly.
+ This issue is public. Export a **sanitized** support bundle before refreshing the affected page when possible, review its preview, and never attach credentials, tokens, cookies, private URLs, a full/unsanitized bundle, or a raw configuration export.
+
- type: checkboxes
id: precheck
attributes:
@@ -15,7 +17,7 @@ body:
options:
- label: I searched existing issues and did not find an open duplicate.
required: true
- - label: I reproduced this on the latest version from this repository (main or latest commit URL).
+ - label: I reproduced this on the latest stable release, or I entered the exact requested dev/commit-specific version below.
required: true
- type: input
@@ -34,6 +36,19 @@ body:
validations:
required: true
+ - type: dropdown
+ id: release_channel
+ attributes:
+ label: Installed release channel
+ options:
+ - Stable / main
+ - Dev testing build
+ - Commit-specific build
+ - Local or manually modified package
+ - Unknown
+ validations:
+ required: true
+
- type: input
id: browser_device
attributes:
@@ -49,13 +64,42 @@ body:
label: Install/update method
options:
- Community Applications
- - plugin install (main URL)
+ - plugin install (stable/main URL)
+ - plugin install (dev URL)
- plugin install (commit-specific URL)
- Local file/manual
- Other
validations:
required: true
+ - type: dropdown
+ id: access_method
+ attributes:
+ label: How do you access Unraid?
+ options:
+ - Direct LAN IP
+ - Local hostname
+ - Reverse proxy or custom domain
+ - Unraid Connect
+ - Tailscale or another VPN
+ - Other or unsure
+ validations:
+ required: true
+
+ - type: dropdown
+ id: docker_view_mode
+ attributes:
+ label: Unraid Docker view mode
+ description: Select Not applicable when this is not a Docker-page issue.
+ options:
+ - Basic View
+ - Advanced View
+ - Tested in both
+ - Not applicable
+ - Unsure
+ validations:
+ required: true
+
- type: dropdown
id: theme_context
attributes:
@@ -82,6 +126,20 @@ body:
validations:
required: true
+ - type: dropdown
+ id: configuration_origin
+ attributes:
+ label: Configuration origin
+ options:
+ - Fresh FolderView Plus configuration
+ - Upgraded from an earlier FolderView Plus version
+ - Imported from FolderView3
+ - Imported from FolderView2
+ - Restored from a backup or export
+ - Unsure
+ validations:
+ required: true
+
- type: dropdown
id: affected_area
attributes:
@@ -89,6 +147,9 @@ body:
multiple: true
options:
- Docker tab rendering
+ - Docker container actions / context menus
+ - Docker API / provider compatibility
+ - Unraid native Docker / prerelease compatibility
- VM tab rendering
- Dashboard widgets
- Settings basic view
@@ -101,6 +162,11 @@ body:
- Safe mode / conflict detection
- Mobile / touch behavior
- Install / update detection
+ - Reverse proxy / request guard
+ - Diagnostics / support bundles
+ - Docker Start Order
+ - Plugin lifecycle / installation / updates
+ - Privacy / security behavior
- Other
validations:
required: true
@@ -142,6 +208,15 @@ body:
validations:
required: true
+ - type: textarea
+ id: relevant_settings
+ attributes:
+ label: Relevant FolderView Plus settings
+ description: List settings related to the problem, such as Preview style, Preview Rows, overflow behavior, context mode, nested-folder visibility, hide-empty folders, Performance Mode, view mode, sorting, or custom colors.
+ placeholder: "Example: Preview Rows = Unlimited; Context = Advanced; parent folder contains one child folder"
+ validations:
+ required: false
+
- type: textarea
id: logs
attributes:
@@ -151,10 +226,32 @@ body:
validations:
required: false
+ - type: dropdown
+ id: support_bundle_status
+ attributes:
+ label: Sanitized support bundle status
+ description: Export from Settings -> FolderView Plus -> Advanced -> Diagnostics and review the preview before sharing.
+ options:
+ - Attached below
+ - Unable to export because Settings or Diagnostics will not load
+ - Unable to export because installation failed
+ - Not applicable to this report
+ - Need instructions
+ validations:
+ required: true
+
- type: textarea
id: attachments
attributes:
label: Attachments (optional but helpful)
- description: Drag and drop screenshots, recordings, export JSON, or diagnostics JSON.
+ description: Drag and drop screenshots, recordings, or a sanitized support bundle. Do not attach a full/unsanitized bundle or raw configuration export publicly.
validations:
required: false
+
+ - type: checkboxes
+ id: privacy_confirmation
+ attributes:
+ label: Public attachment confirmation
+ options:
+ - label: I reviewed my attachments and removed credentials, tokens, cookies, private URLs, unsanitized diagnostics, and raw configuration exports.
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 19329dd2..047fc793 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,14 @@
blank_issues_enabled: false
contact_links:
- - name: Documentation and Install Guide
- url: https://github.com/alexphillips-dev/FolderView-Plus#readme
- about: Check install, update, and usage guidance before opening an issue.
+ - name: FolderView Plus Wiki
+ url: https://github.com/alexphillips-dev/FolderView-Plus/wiki
+ about: Start with the complete installation, update, configuration, and usage documentation.
+ - name: Troubleshooting Guide
+ url: https://github.com/alexphillips-dev/FolderView-Plus/wiki/Troubleshooting
+ about: Check symptom-specific troubleshooting before opening an issue.
+ - name: Diagnostics and Public Sharing Safety
+ url: https://github.com/alexphillips-dev/FolderView-Plus/wiki/Activity-Diagnostics-and-Support
+ about: Learn how to export and review a sanitized support bundle for a public report.
- name: FolderView Plus Support Thread (Unraid Forum)
url: https://forums.unraid.net/topic/197631-plugin-folderview-plus/
about: Plugin support thread for installation, usage, and compatibility questions.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 36b83aa7..616ff44c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -49,6 +49,9 @@ body:
multiple: true
options:
- Docker tab rendering
+ - Docker container actions / context menus
+ - Docker API / provider compatibility
+ - Unraid native Docker / prerelease compatibility
- VM tab rendering
- Dashboard widgets
- Settings basic view
@@ -60,8 +63,12 @@ body:
- Custom icon upload/third-party icons
- Safe mode / conflict detection
- Mobile / touch behavior
+ - Docker Start Order
+ - Diagnostics / support bundles
+ - Reverse proxy / access paths
+ - Privacy / security behavior
- Update/Release process
- - Docs/README
+ - Docs / Wiki / README
- Other
validations:
required: true
diff --git a/.github/ISSUE_TEMPLATE/support_troubleshooting.yml b/.github/ISSUE_TEMPLATE/support_troubleshooting.yml
index 6a4b2c5d..74ad986e 100644
--- a/.github/ISSUE_TEMPLATE/support_troubleshooting.yml
+++ b/.github/ISSUE_TEMPLATE/support_troubleshooting.yml
@@ -8,6 +8,8 @@ body:
value: |
Use this form for setup/help questions. For confirmed bugs, use the Bug report form instead.
+ This issue is public. Export a **sanitized** support bundle when possible, review its preview, and never attach credentials, tokens, cookies, private URLs, a full/unsanitized bundle, or a raw configuration export.
+
- type: dropdown
id: help_topic
attributes:
@@ -21,11 +23,18 @@ body:
- Import/Export behavior
- Backup restore behavior
- Folder ordering
+ - Docker or VM actions / context menus
- Auto-assignment rules
- Custom icon upload or icon packs
- Safe mode / conflict banner
- Mobile/touch layout
- UI/settings behavior
+ - Reverse proxy or custom hostname
+ - Diagnostics or support-bundle export
+ - Docker Start Order
+ - Unraid compatibility
+ - Performance or refresh behavior
+ - Privacy or security behavior
- Other
validations:
required: true
@@ -46,19 +55,61 @@ body:
validations:
required: true
+ - type: dropdown
+ id: release_channel
+ attributes:
+ label: Installed release channel
+ options:
+ - Stable / main
+ - Dev testing build
+ - Commit-specific build
+ - Local or manually modified package
+ - Unknown
+ validations:
+ required: true
+
- type: dropdown
id: install_source
attributes:
label: Install source
options:
- Community Applications
- - plugin install (main URL)
+ - plugin install (stable/main URL)
+ - plugin install (dev URL)
- plugin install (commit-specific URL)
- Local file/manual
- Unsure
validations:
required: true
+ - type: dropdown
+ id: access_method
+ attributes:
+ label: How do you access Unraid?
+ options:
+ - Direct LAN IP
+ - Local hostname
+ - Reverse proxy or custom domain
+ - Unraid Connect
+ - Tailscale or another VPN
+ - Other or unsure
+ validations:
+ required: true
+
+ - type: dropdown
+ id: docker_view_mode
+ attributes:
+ label: Unraid Docker view mode
+ description: Select Not applicable when this is not a Docker-page question.
+ options:
+ - Basic View
+ - Advanced View
+ - Tested in both
+ - Not applicable
+ - Unsure
+ validations:
+ required: true
+
- type: input
id: browser_device
attributes:
@@ -94,6 +145,20 @@ body:
validations:
required: true
+ - type: dropdown
+ id: configuration_origin
+ attributes:
+ label: Configuration origin
+ options:
+ - Fresh FolderView Plus configuration
+ - Upgraded from an earlier FolderView Plus version
+ - Imported from FolderView3
+ - Imported from FolderView2
+ - Restored from a backup or export
+ - Unsure
+ validations:
+ required: true
+
- type: textarea
id: current_behavior
attributes:
@@ -119,6 +184,14 @@ body:
validations:
required: true
+ - type: textarea
+ id: relevant_settings
+ attributes:
+ label: Relevant FolderView Plus settings
+ description: List settings related to the question, such as Preview style, Preview Rows, overflow behavior, context mode, nested-folder visibility, hide-empty folders, Performance Mode, view mode, sorting, or custom colors.
+ validations:
+ required: false
+
- type: textarea
id: logs
attributes:
@@ -128,11 +201,25 @@ body:
validations:
required: false
+ - type: dropdown
+ id: support_bundle_status
+ attributes:
+ label: Sanitized support bundle status
+ description: Export from Settings -> FolderView Plus -> Advanced -> Diagnostics and review the preview before sharing.
+ options:
+ - Attached below
+ - Unable to export because Settings or Diagnostics will not load
+ - Unable to export because installation failed
+ - Not applicable to this request
+ - Need instructions
+ validations:
+ required: true
+
- type: textarea
id: attachments
attributes:
label: Attachments (optional but helpful)
- description: Drag and drop screenshots, recordings, export JSON, or diagnostics JSON.
+ description: Drag and drop screenshots, recordings, or a sanitized support bundle. Do not attach a full/unsanitized bundle or raw configuration export publicly.
validations:
required: false
@@ -141,5 +228,7 @@ body:
attributes:
label: Confirmation
options:
- - label: I reviewed the README install/update section before filing this support request.
+ - label: I reviewed the Wiki and troubleshooting guidance before filing this support request.
+ required: true
+ - label: I reviewed my attachments and removed credentials, tokens, cookies, private URLs, unsanitized diagnostics, and raw configuration exports.
required: true
diff --git a/.github/ISSUE_TEMPLATE/translation.yml b/.github/ISSUE_TEMPLATE/translation.yml
index 2a9304a3..4944eba1 100644
--- a/.github/ISSUE_TEMPLATE/translation.yml
+++ b/.github/ISSUE_TEMPLATE/translation.yml
@@ -4,23 +4,43 @@ title: "[Translation]: "
labels:
- translation
body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for helping improve FolderView Plus. Check the [Translation and Accessibility Wiki page](https://github.com/alexphillips-dev/FolderView-Plus/wiki/Translation-and-Accessibility) for terminology, placeholders, review expectations, and right-to-left guidance.
+
- type: dropdown
id: language
attributes:
label: Language
options:
- - Spanish (es)
- - German (de)
+ - Arabic (ar)
+ - Bengali (bn)
+ - Catalan (ca)
+ - Chinese - Simplified (zh-Hans)
+ - Chinese - Traditional (zh-Hant)
+ - Croatian (hr)
+ - Czech (cs)
+ - Danish (da)
+ - Dutch (nl)
- French (fr)
+ - German (de)
+ - Hungarian (hu)
- Italian (it)
+ - Japanese (ja)
+ - Korean (ko)
+ - Latvian (lv)
+ - Norwegian Bokmål (nb)
- Polish (pl)
- - Dutch (nl)
- Portuguese - Brazil (pt-BR)
- Portuguese - Portugal (pt-PT)
- - Chinese - Simplified (zh-Hans)
- - Japanese (ja)
- - Korean (ko)
- - Another registered locale
+ - Romanian (ro)
+ - Russian (ru)
+ - Spanish (es)
+ - Swedish (sv)
+ - Turkish (tr)
+ - Ukrainian (uk)
+ - Another locale or regional variant
validations:
required: true
- type: dropdown
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index b8aa365e..ec6b4b57 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -12,7 +12,8 @@
## Reporting a Vulnerability
-Please report security issues privately through GitHub Security Advisories:
+Please report security issues privately through GitHub Security Advisories using
+[Report a vulnerability](https://github.com/alexphillips-dev/FolderView-Plus/security/advisories/new):
1. Go to the repository `Security` tab.
2. Select `Report a vulnerability`.
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index 8bca3255..7331360f 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -4,6 +4,9 @@ Use the right channel so issues are handled quickly.
Before opening a report, check:
+- [FolderView Plus Wiki](https://github.com/alexphillips-dev/FolderView-Plus/wiki)
+- [Wiki Troubleshooting](https://github.com/alexphillips-dev/FolderView-Plus/wiki/Troubleshooting)
+- [Wiki Activity, Diagnostics, and Support](https://github.com/alexphillips-dev/FolderView-Plus/wiki/Activity-Diagnostics-and-Support)
- [User Guide](../docs/USER_GUIDE.md)
- [Installation and Upgrades](../docs/INSTALLATION_AND_UPGRADES.md)
- [Migration Guide](../docs/MIGRATION_GUIDE.md)
@@ -20,12 +23,17 @@ Include:
- Unraid version
- plugin version (`/boot/config/plugins/folderview.plus/version`)
+- installed release channel (stable/main, dev, commit-specific, or local/manual)
+- access path (LAN IP, hostname, reverse proxy, Unraid Connect, or VPN)
+- Docker Basic/Advanced View status when the Docker page is involved
+- configuration origin (fresh, upgraded, imported, or restored)
- clear reproduction steps
- expected vs actual result
+- relevant FolderView Plus settings
- relevant screenshots/logs
-- a sanitized support bundle from `Settings -> FolderView Plus -> Advanced -> Diagnostics` when available
+- sanitized support-bundle status and, when available, the bundle from `Settings -> FolderView Plus -> Advanced -> Diagnostics`
-Review the bundle preview before attaching it. Do not post an unsanitized bundle or configuration export publicly.
+Review the bundle preview before attaching it. Do not post credentials, tokens, cookies, private URLs, an unsanitized bundle, or a raw configuration export publicly. If installation, Settings, or Diagnostics does not load, select that reason in the issue form instead of attaching unsafe substitute files.
## Feature Requests
diff --git a/.github/workflows/backmerge-main-to-dev.yml b/.github/workflows/backmerge-main-to-dev.yml
index b82b03d8..7ca21152 100644
--- a/.github/workflows/backmerge-main-to-dev.yml
+++ b/.github/workflows/backmerge-main-to-dev.yml
@@ -7,8 +7,7 @@ on:
workflow_dispatch:
permissions:
- contents: write
- pull-requests: write
+ contents: read
concurrency:
group: folderview-plus-backmerge-main-to-dev
@@ -16,6 +15,9 @@ concurrency:
jobs:
backmerge:
+ permissions:
+ contents: write
+ pull-requests: write
runs-on: ubuntu-latest
timeout-minutes: 90
env:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8c3781c7..2edff3d2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -138,6 +138,7 @@ jobs:
run: echo "seconds=$(( $(date +%s) - ${{ steps.started.outputs.epoch }} ))" >> "${GITHUB_OUTPUT}"
guard-suite:
+ name: CI tests and guards
runs-on: ubuntu-latest
timeout-minutes: 30
needs: detect-changes
diff --git a/.github/workflows/clone-traffic-badge.yml b/.github/workflows/clone-traffic-badge.yml
index e2ac6306..bfe76f90 100644
--- a/.github/workflows/clone-traffic-badge.yml
+++ b/.github/workflows/clone-traffic-badge.yml
@@ -6,25 +6,21 @@ on:
workflow_dispatch:
permissions:
- contents: write
+ contents: read
concurrency:
group: folderview-plus-clone-traffic-badge
cancel-in-progress: false
jobs:
- refresh:
+ collect:
if: github.repository == 'alexphillips-dev/FolderView-Plus'
runs-on: ubuntu-latest
timeout-minutes: 10
+ outputs:
+ badge_json: ${{ steps.badge.outputs.badge_json }}
steps:
- - name: Checkout FolderView Plus
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- with:
- fetch-depth: 1
- persist-credentials: false
-
- name: Build rolling clone badge data
id: badge
env:
@@ -56,19 +52,35 @@ jobs:
};
fs.writeFileSync(badgePath, `${JSON.stringify(badge)}\n`, 'utf8');
NODE
- echo "badge_path=${badge_path}" >> "${GITHUB_OUTPUT}"
+ echo "badge_json=$(cat "${badge_path}")" >> "${GITHUB_OUTPUT}"
+
+ publish:
+ if: github.repository == 'alexphillips-dev/FolderView-Plus'
+ needs: collect
+ permissions:
+ contents: write
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+
+ steps:
- name: Publish rolling clone badge data
env:
GH_TOKEN: ${{ github.token }}
- BADGE_PATH: ${{ steps.badge.outputs.badge_path }}
+ BADGE_JSON: ${{ needs.collect.outputs.badge_json }}
shell: bash
run: |
set -euo pipefail
metrics_dir="${RUNNER_TEMP}/folderview-plus-metrics"
+ badge_path="${RUNNER_TEMP}/clone-traffic.json"
+ if [[ -z "${BADGE_JSON:-}" ]]; then
+ echo "::error title=Clone badge data missing::The isolated collection job returned no badge data."
+ exit 1
+ fi
+ printf '%s\n' "${BADGE_JSON}" > "${badge_path}"
gh auth setup-git
git clone --depth 1 --branch metrics "https://github.com/${GITHUB_REPOSITORY}.git" "${metrics_dir}"
- cp "${BADGE_PATH}" "${metrics_dir}/clone-traffic.json"
+ cp "${badge_path}" "${metrics_dir}/clone-traffic.json"
cd "${metrics_dir}"
if git diff --quiet -- clone-traffic.json; then
echo "Rolling clone count is unchanged."
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index ca44d1b6..7ded1e01 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -16,7 +16,6 @@ on:
permissions:
actions: read
contents: read
- security-events: write
concurrency:
group: folderview-plus-codeql-${{ github.ref }}
@@ -25,6 +24,10 @@ concurrency:
jobs:
analyze:
name: Analyze (JavaScript)
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
runs-on: ubuntu-latest
timeout-minutes: 30
diff --git a/.github/workflows/release-on-main.yml b/.github/workflows/release-on-main.yml
index e3bdc519..9ebb87ab 100644
--- a/.github/workflows/release-on-main.yml
+++ b/.github/workflows/release-on-main.yml
@@ -7,9 +7,7 @@ on:
workflow_dispatch:
permissions:
- contents: write
- id-token: write
- attestations: write
+ contents: read
concurrency:
group: folderview-plus-release
@@ -17,6 +15,10 @@ concurrency:
jobs:
release:
+ permissions:
+ contents: write
+ id-token: write
+ attestations: write
runs-on: ubuntu-latest
timeout-minutes: 90
diff --git a/README.md b/README.md
index c5dee1f2..a3f55ba4 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,14 @@ Unraid's Docker and VM pages can become difficult to scan as your server grows.
| Use the Activity Center, Settings diagnostics, runtime banners, folder editor bootstrap diagnostics, and sanitized support bundles to understand what happened and share useful reports. | Uses shared dark/light theme tokens, modernized Settings and editor surfaces, runtime-safe menu styling, and compatibility guards for Unraid themes and legacy installs. |
|
|
|
+### Custom WebUI profiles
+
+Docker folders can keep multiple named WebUI launch profiles (up to 100 per folder), such as `Media Admin`, `Monitoring`, or `Daily Tools`. Each profile selects direct members of that folder; launching it opens only selected containers that are currently running and have a safe WebUI available. The original **Open all WebUIs** action remains available.
+
+Create and maintain profiles from the folder editor's **WebUI Profiles** section, then launch them from the folder menu under **Open WebUI profile**. Profiles are included with folder clone, export, import, backup, and restore operations, but are intentionally excluded from generic settings copy/paste, templates, and saved defaults because their member selections belong to one specific folder.
+
+> Screenshot placeholder: WebUI Profiles editor and Docker folder profile submenu.
+
## Install
Install from Unraid:
diff --git a/archive/folderview.plus-2026.08.12.02.txz b/archive/folderview.plus-2026.08.12.02.txz
deleted file mode 100644
index 779d2f35..00000000
Binary files a/archive/folderview.plus-2026.08.12.02.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.02.txz.sha256 b/archive/folderview.plus-2026.08.12.02.txz.sha256
deleted file mode 100644
index 640879f3..00000000
--- a/archive/folderview.plus-2026.08.12.02.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-f87ce77ad94ca0c320b74ce37ddfad46d1c3b956accaab260fa5b52c0b83696c folderview.plus-2026.08.12.02.txz
diff --git a/archive/folderview.plus-2026.08.12.03.txz b/archive/folderview.plus-2026.08.12.03.txz
deleted file mode 100644
index fc859033..00000000
Binary files a/archive/folderview.plus-2026.08.12.03.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.03.txz.sha256 b/archive/folderview.plus-2026.08.12.03.txz.sha256
deleted file mode 100644
index 5ee374e8..00000000
--- a/archive/folderview.plus-2026.08.12.03.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-67b7eb711c29b0ab902701106e535f9abc7743f1acd9a4c56d778c525d313f09 folderview.plus-2026.08.12.03.txz
diff --git a/archive/folderview.plus-2026.08.12.04.txz b/archive/folderview.plus-2026.08.12.04.txz
deleted file mode 100644
index 7050cf56..00000000
Binary files a/archive/folderview.plus-2026.08.12.04.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.04.txz.sha256 b/archive/folderview.plus-2026.08.12.04.txz.sha256
deleted file mode 100644
index 0f145f5e..00000000
--- a/archive/folderview.plus-2026.08.12.04.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-3f6f3af6655e2587b59badffcaa6b7211f4f5790ae760fa12d53fa6e98685d8a folderview.plus-2026.08.12.04.txz
diff --git a/archive/folderview.plus-2026.08.12.05.txz b/archive/folderview.plus-2026.08.12.05.txz
deleted file mode 100644
index fa058098..00000000
Binary files a/archive/folderview.plus-2026.08.12.05.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.05.txz.sha256 b/archive/folderview.plus-2026.08.12.05.txz.sha256
deleted file mode 100644
index 5a5d9838..00000000
--- a/archive/folderview.plus-2026.08.12.05.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-4ad7e0839e54e948181bd7d415a7df5bdf51fd11663ac533756ac71287065c79 folderview.plus-2026.08.12.05.txz
diff --git a/archive/folderview.plus-2026.08.12.06.txz b/archive/folderview.plus-2026.08.12.06.txz
deleted file mode 100644
index 161ec70a..00000000
Binary files a/archive/folderview.plus-2026.08.12.06.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.06.txz.sha256 b/archive/folderview.plus-2026.08.12.06.txz.sha256
deleted file mode 100644
index cfb04e77..00000000
--- a/archive/folderview.plus-2026.08.12.06.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-6cc76433f0dd221c9fc2af4667bbffc0e8a30c0544ce01b5afd44194f0ff73c4 folderview.plus-2026.08.12.06.txz
diff --git a/archive/folderview.plus-2026.08.12.07.txz b/archive/folderview.plus-2026.08.12.07.txz
deleted file mode 100644
index 944f5032..00000000
Binary files a/archive/folderview.plus-2026.08.12.07.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.12.07.txz.sha256 b/archive/folderview.plus-2026.08.12.07.txz.sha256
deleted file mode 100644
index 596ab8ca..00000000
--- a/archive/folderview.plus-2026.08.12.07.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-1ff4f2820ca614b560d7ac1a3738f2eef2f5f87098508118bd43828a11bae58a folderview.plus-2026.08.12.07.txz
diff --git a/archive/folderview.plus-2026.08.17.01.txz b/archive/folderview.plus-2026.08.17.01.txz
deleted file mode 100644
index 3d55d969..00000000
Binary files a/archive/folderview.plus-2026.08.17.01.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.17.01.txz.sha256 b/archive/folderview.plus-2026.08.17.01.txz.sha256
deleted file mode 100644
index 1ce70b81..00000000
--- a/archive/folderview.plus-2026.08.17.01.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-693b647ab14c2f8389defe3b9ec348b611ca8e203df2a4057f6e75b1fc86a12d folderview.plus-2026.08.17.01.txz
diff --git a/archive/folderview.plus-2026.08.18.02.txz b/archive/folderview.plus-2026.08.18.02.txz
deleted file mode 100644
index cdf86fe8..00000000
Binary files a/archive/folderview.plus-2026.08.18.02.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.18.02.txz.sha256 b/archive/folderview.plus-2026.08.18.02.txz.sha256
deleted file mode 100644
index 621efc9d..00000000
--- a/archive/folderview.plus-2026.08.18.02.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-b7219d6df88f40c42353417e59c7512895ff9851a0d6fdc758006ca8cf703537 folderview.plus-2026.08.18.02.txz
diff --git a/archive/folderview.plus-2026.08.18.03.txz b/archive/folderview.plus-2026.08.18.03.txz
deleted file mode 100644
index ed7ca5e1..00000000
Binary files a/archive/folderview.plus-2026.08.18.03.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.18.03.txz.sha256 b/archive/folderview.plus-2026.08.18.03.txz.sha256
deleted file mode 100644
index c0507679..00000000
--- a/archive/folderview.plus-2026.08.18.03.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-f09dbae6c0a2f31260872171dfba1d06248a7c2ce0acfd8041c33667de26087f folderview.plus-2026.08.18.03.txz
diff --git a/archive/folderview.plus-2026.08.18.04.txz b/archive/folderview.plus-2026.08.18.04.txz
deleted file mode 100644
index 754a543e..00000000
Binary files a/archive/folderview.plus-2026.08.18.04.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.18.04.txz.sha256 b/archive/folderview.plus-2026.08.18.04.txz.sha256
deleted file mode 100644
index b0e2bb35..00000000
--- a/archive/folderview.plus-2026.08.18.04.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-0ef5f9830453c1a4ed8563c453c4122b134cf51bdbbc6880303910759d58e433 folderview.plus-2026.08.18.04.txz
diff --git a/archive/folderview.plus-2026.08.24.04.txz b/archive/folderview.plus-2026.08.24.04.txz
deleted file mode 100644
index 022cd5ca..00000000
Binary files a/archive/folderview.plus-2026.08.24.04.txz and /dev/null differ
diff --git a/archive/folderview.plus-2026.08.24.04.txz.sha256 b/archive/folderview.plus-2026.08.24.04.txz.sha256
deleted file mode 100644
index aad7e493..00000000
--- a/archive/folderview.plus-2026.08.24.04.txz.sha256
+++ /dev/null
@@ -1 +0,0 @@
-9f4eca819eddf4f5a501137f13b743a259a52fff7a35d6e74278b797641ff259 folderview.plus-2026.08.24.04.txz
diff --git a/archive/folderview.plus-2026.08.25.01.txz b/archive/folderview.plus-2026.08.25.01.txz
new file mode 100644
index 00000000..6dd425f6
Binary files /dev/null and b/archive/folderview.plus-2026.08.25.01.txz differ
diff --git a/archive/folderview.plus-2026.08.25.01.txz.sha256 b/archive/folderview.plus-2026.08.25.01.txz.sha256
new file mode 100644
index 00000000..d4a96673
--- /dev/null
+++ b/archive/folderview.plus-2026.08.25.01.txz.sha256
@@ -0,0 +1 @@
+7dfd5e03ee5f8624f75c3174cea43b9572ce4cb308cc7a6b25dc4762fa4fcd20 folderview.plus-2026.08.25.01.txz
diff --git a/archive/folderview.plus-2026.08.27.01.txz b/archive/folderview.plus-2026.08.27.01.txz
new file mode 100644
index 00000000..84a4186b
Binary files /dev/null and b/archive/folderview.plus-2026.08.27.01.txz differ
diff --git a/archive/folderview.plus-2026.08.27.01.txz.sha256 b/archive/folderview.plus-2026.08.27.01.txz.sha256
new file mode 100644
index 00000000..610ddc2c
--- /dev/null
+++ b/archive/folderview.plus-2026.08.27.01.txz.sha256
@@ -0,0 +1 @@
+500d264b9e8d2e0a2121296be776c27832197bd414bd6dd600cb2d0cddb15b65 folderview.plus-2026.08.27.01.txz
diff --git a/archive/folderview.plus-2026.08.27.02.txz b/archive/folderview.plus-2026.08.27.02.txz
new file mode 100644
index 00000000..5b476c4e
Binary files /dev/null and b/archive/folderview.plus-2026.08.27.02.txz differ
diff --git a/archive/folderview.plus-2026.08.27.02.txz.sha256 b/archive/folderview.plus-2026.08.27.02.txz.sha256
new file mode 100644
index 00000000..2a3d81f0
--- /dev/null
+++ b/archive/folderview.plus-2026.08.27.02.txz.sha256
@@ -0,0 +1 @@
+6216f87629e3f83e211cd2b795c72fd36d60271bbc5208ba5d602626651768bf folderview.plus-2026.08.27.02.txz
diff --git a/archive/folderview.plus-2026.08.27.03.txz b/archive/folderview.plus-2026.08.27.03.txz
new file mode 100644
index 00000000..ae3ed23a
Binary files /dev/null and b/archive/folderview.plus-2026.08.27.03.txz differ
diff --git a/archive/folderview.plus-2026.08.27.03.txz.sha256 b/archive/folderview.plus-2026.08.27.03.txz.sha256
new file mode 100644
index 00000000..68c2a2f3
--- /dev/null
+++ b/archive/folderview.plus-2026.08.27.03.txz.sha256
@@ -0,0 +1 @@
+e5016eb8b88748c8f45540564b35a54af49c559c6ad9d038ec14e389e52de7d0 folderview.plus-2026.08.27.03.txz
diff --git a/archive/folderview.plus-2026.08.27.04.txz b/archive/folderview.plus-2026.08.27.04.txz
new file mode 100644
index 00000000..d16ab8b7
Binary files /dev/null and b/archive/folderview.plus-2026.08.27.04.txz differ
diff --git a/archive/folderview.plus-2026.08.27.04.txz.sha256 b/archive/folderview.plus-2026.08.27.04.txz.sha256
new file mode 100644
index 00000000..0454e830
--- /dev/null
+++ b/archive/folderview.plus-2026.08.27.04.txz.sha256
@@ -0,0 +1 @@
+ae96ef201042e51fcdecfd3b05cb49215cd1a5b523f3a70c5bf3411760c31af9 folderview.plus-2026.08.27.04.txz
diff --git a/archive/folderview.plus-2026.08.27.05.txz b/archive/folderview.plus-2026.08.27.05.txz
new file mode 100644
index 00000000..8ba18375
Binary files /dev/null and b/archive/folderview.plus-2026.08.27.05.txz differ
diff --git a/archive/folderview.plus-2026.08.27.05.txz.sha256 b/archive/folderview.plus-2026.08.27.05.txz.sha256
new file mode 100644
index 00000000..20d64043
--- /dev/null
+++ b/archive/folderview.plus-2026.08.27.05.txz.sha256
@@ -0,0 +1 @@
+5fba9f842534e82d8fc9e7cf69d68d67e737a2448fbf6e8760994dc05f07fca2 folderview.plus-2026.08.27.05.txz
diff --git a/archive/folderview.plus-2026.08.28.01.txz b/archive/folderview.plus-2026.08.28.01.txz
new file mode 100644
index 00000000..2df2bccb
Binary files /dev/null and b/archive/folderview.plus-2026.08.28.01.txz differ
diff --git a/archive/folderview.plus-2026.08.28.01.txz.sha256 b/archive/folderview.plus-2026.08.28.01.txz.sha256
new file mode 100644
index 00000000..1b382c68
--- /dev/null
+++ b/archive/folderview.plus-2026.08.28.01.txz.sha256
@@ -0,0 +1 @@
+e3ed087328f3bec0d5d34d64660eed99359f2cb28c925718d052d5f4084febca folderview.plus-2026.08.28.01.txz
diff --git a/archive/folderview.plus-2026.08.28.02.txz b/archive/folderview.plus-2026.08.28.02.txz
new file mode 100644
index 00000000..6107753e
Binary files /dev/null and b/archive/folderview.plus-2026.08.28.02.txz differ
diff --git a/archive/folderview.plus-2026.08.28.02.txz.sha256 b/archive/folderview.plus-2026.08.28.02.txz.sha256
new file mode 100644
index 00000000..a55e5616
--- /dev/null
+++ b/archive/folderview.plus-2026.08.28.02.txz.sha256
@@ -0,0 +1 @@
+451ee2fa011a7f5c867c9b8f46bc9d5e6783795a637ba72e61e0f0e75fd299d4 folderview.plus-2026.08.28.02.txz
diff --git a/archive/folderview.plus-2026.08.28.03.txz b/archive/folderview.plus-2026.08.28.03.txz
new file mode 100644
index 00000000..052db11e
Binary files /dev/null and b/archive/folderview.plus-2026.08.28.03.txz differ
diff --git a/archive/folderview.plus-2026.08.28.03.txz.sha256 b/archive/folderview.plus-2026.08.28.03.txz.sha256
new file mode 100644
index 00000000..cf81a9e1
--- /dev/null
+++ b/archive/folderview.plus-2026.08.28.03.txz.sha256
@@ -0,0 +1 @@
+255a697535c6b14f95631fa836889a0ddda0c11c388ee2bd313ed21d779e0093 folderview.plus-2026.08.28.03.txz
diff --git a/archive/folderview.plus-2026.08.28.04.txz b/archive/folderview.plus-2026.08.28.04.txz
new file mode 100644
index 00000000..0782d3ba
Binary files /dev/null and b/archive/folderview.plus-2026.08.28.04.txz differ
diff --git a/archive/folderview.plus-2026.08.28.04.txz.sha256 b/archive/folderview.plus-2026.08.28.04.txz.sha256
new file mode 100644
index 00000000..e7ebd488
--- /dev/null
+++ b/archive/folderview.plus-2026.08.28.04.txz.sha256
@@ -0,0 +1 @@
+4c9da7f4379224d2b65ff9957c08ab5e996d6c8d7082762504f7415ef1f95194 folderview.plus-2026.08.28.04.txz
diff --git a/archive/folderview.plus-2026.08.28.05.txz b/archive/folderview.plus-2026.08.28.05.txz
new file mode 100644
index 00000000..3a2cd91f
Binary files /dev/null and b/archive/folderview.plus-2026.08.28.05.txz differ
diff --git a/archive/folderview.plus-2026.08.28.05.txz.sha256 b/archive/folderview.plus-2026.08.28.05.txz.sha256
new file mode 100644
index 00000000..952d2ef3
--- /dev/null
+++ b/archive/folderview.plus-2026.08.28.05.txz.sha256
@@ -0,0 +1 @@
+f71ee236840caadc58c6e22630a311670fa39d39f48293d086182cf0bb4dae88 folderview.plus-2026.08.28.05.txz
diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md
index 4d6212f5..2552eb41 100644
--- a/docs/USER_GUIDE.md
+++ b/docs/USER_GUIDE.md
@@ -61,15 +61,39 @@ The Docker page View menu provides three supported modes:
Changing views does not delete folder configuration. Use `Reset view` to clear temporary toolbar filters and return to the normal unfiltered state.
+Each Docker folder menu includes four quick actions: focus, pin, lock, and hide. Hiding a parent hides its complete nested branch and member rows from FolderView without deleting configuration or changing assignments. Use the temporary `Undo` notice immediately after hiding, or open `View -> Hidden folders` to reveal hidden rows. Revealed rows are dimmed and labeled `Hidden`; restore one from its folder menu or choose `Restore all hidden folders`. Hidden folders remain visible in Host list mode because that mode intentionally restores Unraid's native table.
+
VM and Dashboard surfaces use the same saved Docker or VM folder configuration where applicable. Runtime state is reconciled incrementally so start, stop, pause, resume, and update results can update without rebuilding the entire page.
## Folder actions
+### Custom WebUI profiles for Docker folders
+
+Use a custom WebUI profile when you regularly open only part of a Docker folder. Profiles do not store URLs. They store selected direct-member names and resolve each container's current safe WebUI address and running state at launch time.
+
+To create a profile:
+
+1. Edit a Docker folder and open **WebUI Profiles**.
+2. Select **Add profile** and enter a unique name.
+3. Select one or more direct folder members. Search, **Select all with WebUI**, and **Clear selection** are available for larger folders.
+4. Save the folder. Empty profiles and duplicate names are rejected.
+5. Open the folder menu on the Docker page, choose **Open WebUI profile**, and select the profile. Its ready count shows how many selected WebUIs can open now.
+
+Only selected containers that are running, not paused, and expose a safe WebUI are opened. Stopped, paused, removed, or currently unavailable members remain selected for later but are skipped. If a browser blocks one or more tabs, allow popups for the Unraid host and retry the same profile. The original **Open all WebUIs** action still opens every ready WebUI in that folder.
+
+Use **Manage WebUI profiles** in the folder menu to return directly to the editor section. Profiles can be duplicated, reordered, or deleted. Each folder supports up to 100 profiles with up to 250 selected members per profile. Container renames are reconciled automatically when FolderView Plus can uniquely identify the renamed container; unresolved members are labeled unavailable so the selection can be corrected manually.
+
+Profiles follow a full folder clone and are preserved by folder export/import and backup/restore. Generic **Copy Folder Settings**, reusable templates, smart defaults, and saved folder defaults omit them so container-specific selections are not applied to unrelated folders.
+
+> Screenshot placeholder: WebUI Profiles editor showing named profiles and selected members.
+
+> Screenshot placeholder: Docker folder menu showing Open all WebUIs, Open WebUI profile, ready counts, and Manage WebUI profiles.
+
The folder action sheet groups actions by purpose rather than placing every operation in one flat menu. Available actions depend on folder type, hierarchy position, lock state, and current member state.
Common actions include:
-- Edit, pin, focus, lock, expand, or collapse a folder or branch.
+- Edit, pin, focus, lock, hide, expand, or collapse a folder or branch.
- Start, stop, pause, resume, restart, or update eligible members.
- Move a folder within its current level, under another folder, or back to the root.
- Clone, export, import into, scan, repair, or delete a folder branch.
diff --git a/docs/docker-runtime-architecture.md b/docs/docker-runtime-architecture.md
index 778b1a4f..f3731c0b 100644
--- a/docs/docker-runtime-architecture.md
+++ b/docs/docker-runtime-architecture.md
@@ -87,7 +87,7 @@ The runtime still uses an internal threshold state while resolving Adaptive and
- Bootstrap repeats host detection after parsing, then enables legacy styles, loads legacy custom overrides in sequence, and finally loads `docker.js` only for a confirmed complete table contract.
- Native and unknown generations keep every legacy stylesheet inert and return before custom overrides, adapters, hooks, requests, observers, or jQuery prefilters are created.
- Unraid lifecycle globals (`loadlist`, `listview`, `openDocker`, `eventControl`, and `addDockerContainerContext`) are wrapped idempotently by the adapter, with compatibility aliases retained for host/plugin interoperability.
-- Context menu quick actions (Focus/Pin/Lock) are enhanced through the adapter rather than ad-hoc DOM logic.
+- Context menu quick actions (Focus/Pin/Lock/Hide) are enhanced through the adapter rather than ad-hoc DOM logic. Hidden folder IDs persist in normalized Docker preferences, parent visibility applies to the complete branch, and the View menu provides temporary reveal and restore-all recovery paths.
- CSS layout constants use tokenized variables with hard-coded fallback values to preserve legacy contracts.
- Preview hydration diagnostics record unavailable WebUI slots, disconnected targets, absolute movement, row movement, and movement relative to the owning row without retaining container or folder identities.
- The main Docker runtime line budget is ratcheted from 8,839 to 8,028 lines. Column layout/control moved behind a lifecycle-owned factory, while reusable ordering and live-refresh seams reduced duplicate ownership across Docker, VM, and Dashboard.
diff --git a/docs/releases/2026.08.25.01.md b/docs/releases/2026.08.25.01.md
new file mode 100644
index 00000000..8d611972
--- /dev/null
+++ b/docs/releases/2026.08.25.01.md
@@ -0,0 +1,5 @@
+- Fix: Open the same child-folder action menu from a collapsed parent preview when using a normal click, right-click, touch activation, Enter, or Space.
+- Accessibility: Position keyboard-opened menus beside the child-folder preview chip, focus the first menu action, and retain the existing Expand, Edit, and Open folder actions.
+- Diagnostics: Add bounded child-folder preview render, binding, menu-attempt, success, failure, and input-method counters to sanitized support bundles.
+- Privacy: Keep child-folder preview diagnostics free of folder names, identifiers, selectors, URLs, and pointer coordinates.
+- Test: Cover child-folder menu activation after repeated multi-row preview reconciliation and verify mouse, keyboard, context-menu, touch, focus, geometry, and diagnostic behavior in the browser fixture suite.
diff --git a/docs/releases/2026.08.27.01.md b/docs/releases/2026.08.27.01.md
new file mode 100644
index 00000000..919238f7
--- /dev/null
+++ b/docs/releases/2026.08.27.01.md
@@ -0,0 +1,6 @@
+- Fix: Use green as the default started-status color in the folder editor and across Docker, VM, and Dashboard folder surfaces.
+- Compatibility: Migrate the previous unmodified white started-status default to green while preserving white when it is explicitly selected and saved in the modern folder editor.
+- Fix: Show a visible pending spinner on compact multi-row Docker previews while a container lifecycle action, including Restart, is in progress.
+- Fix: Keep Restart pending through the initial already-running snapshot, then restore the container preview to its current started, paused, or stopped state after reconciliation.
+- Diagnostics: Record privacy-safe counts for lifecycle preview surfaces and pending or restored status icons without exporting container identities.
+- Test: Cover legacy and explicit status-color behavior, compact-preview lifecycle feedback, restart timing, browser fixtures, responsive themes, and large-runtime performance budgets.
diff --git a/docs/releases/2026.08.27.02.md b/docs/releases/2026.08.27.02.md
new file mode 100644
index 00000000..c0de9457
--- /dev/null
+++ b/docs/releases/2026.08.27.02.md
@@ -0,0 +1,6 @@
+- Fix: Restore native-style folder status presentation with green started indicators and independently white status text by default.
+- Feature: Add a Text color picker to the modern folder editor Status section so status labels can be customized separately from Started, Paused, and Stopped indicators.
+- Compatibility: Apply the independent status text color across Docker folders and previews, VM folders, Dashboard folders, inherited defaults, starter templates, and existing folders without requiring migration.
+- Diagnostics: Include the normalized status text color in privacy-safe folder integrity diagnostics.
+- Localization: Translate the new status text control and updated guidance across all 27 supported locales.
+- Test: Cover status text normalization, editor persistence, runtime theme separation, localization, browser fixtures, responsive themes, and large-runtime performance budgets.
diff --git a/docs/releases/2026.08.27.03.md b/docs/releases/2026.08.27.03.md
new file mode 100644
index 00000000..9a2ba3dc
--- /dev/null
+++ b/docs/releases/2026.08.27.03.md
@@ -0,0 +1,4 @@
+- Fix: Show the same pending lifecycle spinner for Docker containers in single-row and multi-row folder previews.
+- Fix: Keep Start, Resume, and Restart feedback on the folder's Started color instead of temporarily using the Paused color; Pause and Stop continue to use their matching status colors.
+- Compatibility: Honor each folder's customized Started, Paused, and Stopped indicator colors while lifecycle actions are pending.
+- Test: Cover single-row lifecycle identity, action-specific pending colors, spinner cleanup, browser fixtures, responsive themes, and large-runtime performance budgets.
diff --git a/docs/releases/2026.08.27.04.md b/docs/releases/2026.08.27.04.md
new file mode 100644
index 00000000..60532ae2
--- /dev/null
+++ b/docs/releases/2026.08.27.04.md
@@ -0,0 +1,4 @@
+- Fix: Restore container context actions in collapsed parent-folder previews when the folder contains child folders.
+- Compatibility: Apply the correct Default or Advanced context behavior after nested previews are rebuilt, including Preview Rows 1+, Unlimited, and Unraid Basic or Advanced View.
+- Compatibility: Preserve the parent folder's popup settings and keep child-folder preview actions independent.
+- Test: Cover nested parent container actions in both context modes with Unraid Advanced View enabled, plus child-folder actions, browser fixtures, and large-runtime performance budgets.
diff --git a/docs/releases/2026.08.27.05.md b/docs/releases/2026.08.27.05.md
new file mode 100644
index 00000000..1b0148f0
--- /dev/null
+++ b/docs/releases/2026.08.27.05.md
@@ -0,0 +1,7 @@
+- Feature: Add a fourth **Hide** quick action to Docker folder menus. Hiding a parent folder temporarily removes its complete nested branch from FolderView without deleting folders, assignments, rules, or settings.
+- UX: Add a hidden-folder count, temporary **Show hidden folders** view, **Restore all hidden folders** action, and a 10-second **Undo** notice so hidden rows always remain recoverable.
+- UX: Dim temporarily revealed folders and mark them as hidden while leaving Unraid's native Host list unchanged.
+- Reliability: Persist hidden-folder choices through the existing conflict-safe preference flow, preserve the newest choice during rapid or cross-tab updates, clear focus when its folder is hidden, and prune references to deleted folders.
+- Diagnostics: Add privacy-safe hidden-folder counts, hashed identifiers, integrity checks, and effective visibility state to runtime diagnostics and support bundles.
+- Localization: Add the hidden-folder interface text to all 27 supported locale catalogs.
+- Test: Cover nested branch hiding, reveal, restore-all, undo, preference conflicts, diagnostics, accessibility, browser rendering, themes, and runtime performance.
diff --git a/docs/releases/2026.08.28.01.md b/docs/releases/2026.08.28.01.md
new file mode 100644
index 00000000..87853ce6
--- /dev/null
+++ b/docs/releases/2026.08.28.01.md
@@ -0,0 +1,4 @@
+- Fix: Show the 10-second Docker folder **Undo** notice immediately when a folder is hidden instead of waiting for preference persistence to finish.
+- Reliability: Keep the optimistic hide and Undo actions conflict-safe, dismiss only the matching notice during restoration, and remove a failed hide notice before rolling the folder state back.
+- UI/UX: Style Undo, Show hidden folders, and Restore all hidden folders with scoped theme tokens so Unraid host button rules cannot force an orange outline.
+- Test: Cover deferred preference saves, save failure rollback, folder-aware notice dismissal, and 552 browser combinations across light and dark themes, desktop and mobile viewports, and Chromium, Firefox, and WebKit.
diff --git a/docs/releases/2026.08.28.02.md b/docs/releases/2026.08.28.02.md
new file mode 100644
index 00000000..f7139d37
--- /dev/null
+++ b/docs/releases/2026.08.28.02.md
@@ -0,0 +1,8 @@
+- Feature: Add reusable custom WebUI profiles to each Docker folder so one menu action can open only the selected container WebUIs.
+- UI/UX: Add profile creation, naming, duplication, ordering, search, WebUI-aware selection, unavailable-member visibility, and deletion to the modern Docker folder editor, with responsive keyboard-accessible controls.
+- Reliability: Resolve each selected container's current WebUI only when a profile is launched, open direct running and unpaused members only, retain renamed members through identity reconciliation, and fail closed when a profile no longer exists.
+- Compatibility: Preserve folder-specific profiles through clone, full export/import, backup, and restore while excluding them from generic settings copy/paste, templates, and saved defaults that may target different containers.
+- Privacy: Add support-bundle profile health counts without exporting profile names, container names, or WebUI addresses.
+- Localization: Translate the custom WebUI profile editor and Docker menu across every supported locale.
+- Docs: Document profile setup, launch behavior, popup handling, migration behavior, and current screenshot placeholders in the README and user guide.
+- Test: Cover browser, mobile, keyboard, theme, persistence, launch eligibility, popup safety, rename, transfer, diagnostics, localization, and large-library performance behavior using isolated fixtures.
diff --git a/docs/releases/2026.08.28.03.md b/docs/releases/2026.08.28.03.md
new file mode 100644
index 00000000..109763ac
--- /dev/null
+++ b/docs/releases/2026.08.28.03.md
@@ -0,0 +1,3 @@
+- UI/UX: Style every custom WebUI profile editor action with FolderView Plus controls instead of Unraid's default orange button treatment.
+- Accessibility: Preserve visible keyboard focus and use the plugin's theme-aware text, surface, hover, active, disabled, and shadow tokens across dark and light themes.
+- Test: Verify custom WebUI profile buttons override hostile host defaults and follow the final dark and light theme palettes in an isolated browser fixture.
diff --git a/docs/releases/2026.08.28.04.md b/docs/releases/2026.08.28.04.md
new file mode 100644
index 00000000..497ce29a
--- /dev/null
+++ b/docs/releases/2026.08.28.04.md
@@ -0,0 +1,4 @@
+- Fix: Keep hidden-folder restore labels and custom WebUI profile warnings registered as active localized UI text.
+- Reliability: Prevent strict localization validation and main-to-dev back-merges from rejecting those live Docker translation keys.
+- Security: Reject malformed non-string folder names and container identifiers without invoking attacker-controlled coercion hooks.
+- Test: Rebuild all 27 locale catalogs and verify hidden-folder behavior, localization coverage, and security workflow contracts.
diff --git a/docs/releases/2026.08.28.05.md b/docs/releases/2026.08.28.05.md
new file mode 100644
index 00000000..cbb8dcad
--- /dev/null
+++ b/docs/releases/2026.08.28.05.md
@@ -0,0 +1,15 @@
+- Feature: Add a fourth **Hide** quick action to Docker folder menus, including nested-branch hiding, a temporary **Show hidden folders** view, **Restore all hidden folders**, and an immediate 10-second **Undo** action without deleting folder configuration or changing Unraid's native Host list.
+- Feature: Add reusable custom WebUI profiles to each Docker folder so any number of named profiles can open only the selected running, unpaused container WebUIs, with create, duplicate, reorder, search, unavailable-member, and delete controls in the modern folder editor.
+- Fix: Restore reliable container and child-folder context menus across single-row, multi-row, nested, collapsed, Preview Rows 1+, Unlimited, and Unraid Basic or Advanced View layouts, including mouse, right-click, touch, Enter, and Space activation.
+- Fix: Show consistent lifecycle progress on single-row and multi-row Docker previews, keep Restart pending through its initial running snapshot, and use the action's destination status color until reconciliation completes.
+- UI/UX: Use Unraid's native green started indicator with white status text by default, add a separate **Text** color picker, and apply customized Started, Paused, Stopped, and Text colors consistently across Docker, VM, Dashboard, inherited defaults, and starter templates.
+- UI/UX: Make hidden-folder recovery, Undo, and custom WebUI profile controls theme-aware, responsive, and keyboard accessible instead of inheriting Unraid's orange button treatment.
+- Reliability: Persist hidden-folder choices through conflict-safe preference updates, preserve profiles through clone, export/import, backup, restore, and unique member renames, and keep folder-specific profiles out of generic settings transfers and defaults that may target unrelated containers.
+- Compatibility: Preserve explicitly selected white started indicators while migrating only the old unmodified white default to green, and acknowledge Unraid API v4.37.3 while keeping current legacy Docker behavior and future API capability monitoring bounded and fail-closed.
+- Security: Reject malformed non-string folder names and container identifiers without invoking attacker-controlled coercion hooks, strengthen property-based input coverage, and tighten repository security-policy, CodeQL, workflow-permission, package-trust, and release contracts.
+- Privacy: Extend sanitized diagnostics with aggregate context-menu, lifecycle, hidden-folder, and WebUI-profile health evidence while withholding folder names, container names, profile names, URLs, selectors, coordinates, and other runtime identities.
+- Localization: Translate the new hidden-folder, WebUI-profile, and status-text interfaces across all 27 supported locales and keep dynamic Docker menu labels covered by strict localization validation.
+- UX: Improve GitHub bug, support, feature, and translation intake forms with clearer reproduction, version, branch, diagnostics, privacy, compatibility, and testing fields.
+- Maintenance: Add proactive Unraid/API compatibility checks, dependency and SBOM vulnerability monitoring, scheduled workflow watchdogs, manual CodeQL recovery checks, exact-tool CodeQL alert filtering, and validated main-to-dev back-merge ordering without introducing live-Unraid automation.
+- Compatibility: No manual migration is required. Existing folders, assignments, rules, colors, backups, and imports remain compatible; hidden-folder state is reversible, and custom WebUI profiles remain scoped to their Docker folder.
+- Test: Expand isolated browser, mobile, keyboard, theme, localization, fuzz, security, diagnostics, workflow, and large-library performance coverage for every behavior in this release.
diff --git a/docs/sbom.cdx.json b/docs/sbom.cdx.json
index 723bcb79..6a2f0fd5 100644
--- a/docs/sbom.cdx.json
+++ b/docs/sbom.cdx.json
@@ -1,13 +1,13 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
- "serialNumber": "urn:uuid:8d6ef646-c87b-8601-8d30-57908769da9d",
+ "serialNumber": "urn:uuid:e1de95ce-ed49-8928-999c-a260ac5c59c3",
"version": 1,
"metadata": {
"component": {
"type": "application",
"name": "FolderView Plus",
- "version": "2026.08.24.04",
+ "version": "2026.08.28.05",
"properties": [
{
"name": "folderview-plus:runtime-dependencies",
@@ -569,6 +569,20 @@
],
"purl": "pkg:npm/escalade@3.2.0"
},
+ {
+ "type": "library",
+ "name": "fast-check",
+ "version": "4.9.0",
+ "scope": "optional",
+ "licenses": [
+ {
+ "license": {
+ "id": "MIT"
+ }
+ }
+ ],
+ "purl": "pkg:npm/fast-check@4.9.0"
+ },
{
"type": "library",
"name": "find-up",
@@ -1099,6 +1113,20 @@
],
"purl": "pkg:npm/playwright@1.62.1"
},
+ {
+ "type": "library",
+ "name": "pure-rand",
+ "version": "8.4.2",
+ "scope": "optional",
+ "licenses": [
+ {
+ "license": {
+ "id": "MIT"
+ }
+ }
+ ],
+ "purl": "pkg:npm/pure-rand@8.4.2"
+ },
{
"type": "library",
"name": "semver",
diff --git a/docs/security/csp-readiness.json b/docs/security/csp-readiness.json
index f93b851e..0c690a25 100644
--- a/docs/security/csp-readiness.json
+++ b/docs/security/csp-readiness.json
@@ -3,7 +3,7 @@
"mode": "report-only-shared-unraid-document",
"scope": {
"scannedRoot": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus",
- "scannedFiles": 234,
+ "scannedFiles": 238,
"exclusions": [
"third-party icon asset pack",
"vendored scripts",
@@ -21,7 +21,7 @@
"totals": {
"inlineEventAttributes": 0,
"inlineScriptBlocks": 0,
- "externalScriptTags": 205,
+ "externalScriptTags": 211,
"inlineStyleBlocks": 0,
"inlineStyleAttributes": 0,
"evalCalls": 0,
@@ -72,7 +72,7 @@
"htmlStringSinkInventory": [
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js",
- "line": 1238,
+ "line": 1257,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -82,7 +82,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js",
- "line": 1249,
+ "line": 1268,
"sink": ".innerHTML =",
"risk": "reviewed",
"dataClass": "static-or-controlled-ui-template",
@@ -92,7 +92,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js",
- "line": 1294,
+ "line": 1313,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -102,7 +102,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js",
- "line": 3395,
+ "line": 3443,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -112,7 +112,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js",
- "line": 274,
+ "line": 312,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -152,7 +152,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js",
- "line": 471,
+ "line": 470,
"sink": "insertAdjacentHTML(",
"risk": "reviewed",
"dataClass": "static-or-controlled-ui-template",
@@ -162,7 +162,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js",
- "line": 485,
+ "line": 484,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -442,7 +442,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js",
- "line": 85,
+ "line": 87,
"sink": "insertAdjacentHTML(",
"risk": "reviewed",
"dataClass": "static-or-controlled-ui-template",
@@ -452,7 +452,7 @@
},
{
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js",
- "line": 273,
+ "line": 275,
"sink": ".innerHTML =",
"risk": "high",
"dataClass": "persisted-or-runtime-data",
@@ -486,7 +486,7 @@
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page",
"inlineEventAttributes": 0,
"inlineScriptBlocks": 0,
- "externalScriptTags": 31,
+ "externalScriptTags": 32,
"inlineStyleBlocks": 0,
"inlineStyleAttributes": 0,
"evalCalls": 0,
@@ -510,7 +510,7 @@
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Dashboard.page",
"inlineEventAttributes": 0,
"inlineScriptBlocks": 0,
- "externalScriptTags": 46,
+ "externalScriptTags": 47,
"inlineStyleBlocks": 0,
"inlineStyleAttributes": 0,
"evalCalls": 0,
@@ -522,7 +522,7 @@
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Docker.page",
"inlineEventAttributes": 0,
"inlineScriptBlocks": 0,
- "externalScriptTags": 64,
+ "externalScriptTags": 67,
"inlineStyleBlocks": 0,
"inlineStyleAttributes": 0,
"evalCalls": 0,
@@ -534,7 +534,7 @@
"path": "src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.VMs.page",
"inlineEventAttributes": 0,
"inlineScriptBlocks": 0,
- "externalScriptTags": 38,
+ "externalScriptTags": 39,
"inlineStyleBlocks": 0,
"inlineStyleAttributes": 0,
"evalCalls": 0,
diff --git a/folderview.plus.plg b/folderview.plus.plg
index a2ad93b3..9e179046 100644
--- a/folderview.plus.plg
+++ b/folderview.plus.plg
@@ -6,9 +6,9 @@
-
-
-
+
+
+
@@ -18,17 +18,103 @@
-###2026.08.24.04
-- Fix: Keep Docker preview context menus clickable in every visible row when Preview Rows is set to 2, 3, 4, or Unlimited, including after repeated layout reconciliation.
-- Compatibility: Route Default-context preview activation through the original Unraid Docker control without copying host IDs or inline handlers, while preserving WebUI, logs, and console quick actions.
-- Accessibility: Support first-click mouse activation from the preview icon, name, and status plus Enter and Space keyboard activation.
-- Fix: Preserve the correct started, paused, and stopped styling on compact Docker preview status text and icons.
-- Diagnostics: Add a short-lived, privacy-safe support-bundle summary of preview row mode and numeric row position, bridge binding and handler integrity, activation category, input method, dispatch outcomes, and bounded failure reasons.
-- Privacy: Exclude folder and container identities, selectors, URLs, and pointer coordinates from preview context diagnostics.
-- Maintenance: Add daily public-contract monitoring for Unraid stable and prerelease versions, PHP runtimes, Docker/VM/Dashboard and plugin-manager surfaces, Docker API/native-page signals, and Community Applications publication without live-Unraid credentials.
-- Security: Add weekly OSV scanning of the generated SBOM, scheduled-workflow health coverage, and tool-scoped CodeQL alert enforcement so unrelated Scorecard findings do not fail CodeQL analysis.
-- Compatibility: Refresh the reviewed Unraid/API baseline while retaining native-Docker safe mode and human approval for every upstream baseline change.
-- Test: Cover multi-row and Unlimited preview interaction, repeated finalization, handler-loss detection, persistence, accessibility, status styling, and privacy boundaries in Chromium and Firefox.
+###2026.08.28.05
+- Feature: Add a fourth **Hide** quick action to Docker folder menus, including nested-branch hiding, a temporary **Show hidden folders** view, **Restore all hidden folders**, and an immediate 10-second **Undo** action without deleting folder configuration or changing Unraid's native Host list.
+- Feature: Add reusable custom WebUI profiles to each Docker folder so any number of named profiles can open only the selected running, unpaused container WebUIs, with create, duplicate, reorder, search, unavailable-member, and delete controls in the modern folder editor.
+- Fix: Restore reliable container and child-folder context menus across single-row, multi-row, nested, collapsed, Preview Rows 1+, Unlimited, and Unraid Basic or Advanced View layouts, including mouse, right-click, touch, Enter, and Space activation.
+- Fix: Show consistent lifecycle progress on single-row and multi-row Docker previews, keep Restart pending through its initial running snapshot, and use the action's destination status color until reconciliation completes.
+- UI/UX: Use Unraid's native green started indicator with white status text by default, add a separate **Text** color picker, and apply customized Started, Paused, Stopped, and Text colors consistently across Docker, VM, Dashboard, inherited defaults, and starter templates.
+- UI/UX: Make hidden-folder recovery, Undo, and custom WebUI profile controls theme-aware, responsive, and keyboard accessible instead of inheriting Unraid's orange button treatment.
+- Reliability: Persist hidden-folder choices through conflict-safe preference updates, preserve profiles through clone, export/import, backup, restore, and unique member renames, and keep folder-specific profiles out of generic settings transfers and defaults that may target unrelated containers.
+- Compatibility: Preserve explicitly selected white started indicators while migrating only the old unmodified white default to green, and acknowledge Unraid API v4.37.3 while keeping current legacy Docker behavior and future API capability monitoring bounded and fail-closed.
+- Security: Reject malformed non-string folder names and container identifiers without invoking attacker-controlled coercion hooks, strengthen property-based input coverage, and tighten repository security-policy, CodeQL, workflow-permission, package-trust, and release contracts.
+- Privacy: Extend sanitized diagnostics with aggregate context-menu, lifecycle, hidden-folder, and WebUI-profile health evidence while withholding folder names, container names, profile names, URLs, selectors, coordinates, and other runtime identities.
+- Localization: Translate the new hidden-folder, WebUI-profile, and status-text interfaces across all 27 supported locales and keep dynamic Docker menu labels covered by strict localization validation.
+- UX: Improve GitHub bug, support, feature, and translation intake forms with clearer reproduction, version, branch, diagnostics, privacy, compatibility, and testing fields.
+- Maintenance: Add proactive Unraid/API compatibility checks, dependency and SBOM vulnerability monitoring, scheduled workflow watchdogs, manual CodeQL recovery checks, exact-tool CodeQL alert filtering, and validated main-to-dev back-merge ordering without introducing live-Unraid automation.
+- Compatibility: No manual migration is required. Existing folders, assignments, rules, colors, backups, and imports remain compatible; hidden-folder state is reversible, and custom WebUI profiles remain scoped to their Docker folder.
+- Test: Expand isolated browser, mobile, keyboard, theme, localization, fuzz, security, diagnostics, workflow, and large-library performance coverage for every behavior in this release.
+
+
+###2026.08.28.04
+- Fix: Keep hidden-folder restore labels and custom WebUI profile warnings registered as active localized UI text.
+- Reliability: Prevent strict localization validation and main-to-dev back-merges from rejecting those live Docker translation keys.
+- Security: Reject malformed non-string folder names and container identifiers without invoking attacker-controlled coercion hooks.
+- Test: Rebuild all 27 locale catalogs and verify hidden-folder behavior, localization coverage, and security workflow contracts.
+
+
+###2026.08.28.03
+- UI/UX: Style every custom WebUI profile editor action with FolderView Plus controls instead of Unraid's default orange button treatment.
+- Accessibility: Preserve visible keyboard focus and use the plugin's theme-aware text, surface, hover, active, disabled, and shadow tokens across dark and light themes.
+- Test: Verify custom WebUI profile buttons override hostile host defaults and follow the final dark and light theme palettes in an isolated browser fixture.
+
+
+###2026.08.28.02
+- Feature: Add reusable custom WebUI profiles to each Docker folder so one menu action can open only the selected container WebUIs.
+- UI/UX: Add profile creation, naming, duplication, ordering, search, WebUI-aware selection, unavailable-member visibility, and deletion to the modern Docker folder editor, with responsive keyboard-accessible controls.
+- Reliability: Resolve each selected container's current WebUI only when a profile is launched, open direct running and unpaused members only, retain renamed members through identity reconciliation, and fail closed when a profile no longer exists.
+- Compatibility: Preserve folder-specific profiles through clone, full export/import, backup, and restore while excluding them from generic settings copy/paste, templates, and saved defaults that may target different containers.
+- Privacy: Add support-bundle profile health counts without exporting profile names, container names, or WebUI addresses.
+- Localization: Translate the custom WebUI profile editor and Docker menu across every supported locale.
+- Docs: Document profile setup, launch behavior, popup handling, migration behavior, and current screenshot placeholders in the README and user guide.
+- Test: Cover browser, mobile, keyboard, theme, persistence, launch eligibility, popup safety, rename, transfer, diagnostics, localization, and large-library performance behavior using isolated fixtures.
+
+
+###2026.08.28.01
+- Fix: Show the 10-second Docker folder **Undo** notice immediately when a folder is hidden instead of waiting for preference persistence to finish.
+- Reliability: Keep the optimistic hide and Undo actions conflict-safe, dismiss only the matching notice during restoration, and remove a failed hide notice before rolling the folder state back.
+- UI/UX: Style Undo, Show hidden folders, and Restore all hidden folders with scoped theme tokens so Unraid host button rules cannot force an orange outline.
+- Test: Cover deferred preference saves, save failure rollback, folder-aware notice dismissal, and 552 browser combinations across light and dark themes, desktop and mobile viewports, and Chromium, Firefox, and WebKit.
+
+
+###2026.08.27.05
+- Feature: Add a fourth **Hide** quick action to Docker folder menus. Hiding a parent folder temporarily removes its complete nested branch from FolderView without deleting folders, assignments, rules, or settings.
+- UX: Add a hidden-folder count, temporary **Show hidden folders** view, **Restore all hidden folders** action, and a 10-second **Undo** notice so hidden rows always remain recoverable.
+- UX: Dim temporarily revealed folders and mark them as hidden while leaving Unraid's native Host list unchanged.
+- Reliability: Persist hidden-folder choices through the existing conflict-safe preference flow, preserve the newest choice during rapid or cross-tab updates, clear focus when its folder is hidden, and prune references to deleted folders.
+- Diagnostics: Add privacy-safe hidden-folder counts, hashed identifiers, integrity checks, and effective visibility state to runtime diagnostics and support bundles.
+- Localization: Add the hidden-folder interface text to all 27 supported locale catalogs.
+- Test: Cover nested branch hiding, reveal, restore-all, undo, preference conflicts, diagnostics, accessibility, browser rendering, themes, and runtime performance.
+
+
+###2026.08.27.04
+- Fix: Restore container context actions in collapsed parent-folder previews when the folder contains child folders.
+- Compatibility: Apply the correct Default or Advanced context behavior after nested previews are rebuilt, including Preview Rows 1+, Unlimited, and Unraid Basic or Advanced View.
+- Compatibility: Preserve the parent folder's popup settings and keep child-folder preview actions independent.
+- Test: Cover nested parent container actions in both context modes with Unraid Advanced View enabled, plus child-folder actions, browser fixtures, and large-runtime performance budgets.
+
+
+###2026.08.27.03
+- Fix: Show the same pending lifecycle spinner for Docker containers in single-row and multi-row folder previews.
+- Fix: Keep Start, Resume, and Restart feedback on the folder's Started color instead of temporarily using the Paused color; Pause and Stop continue to use their matching status colors.
+- Compatibility: Honor each folder's customized Started, Paused, and Stopped indicator colors while lifecycle actions are pending.
+- Test: Cover single-row lifecycle identity, action-specific pending colors, spinner cleanup, browser fixtures, responsive themes, and large-runtime performance budgets.
+
+
+###2026.08.27.02
+- Fix: Restore native-style folder status presentation with green started indicators and independently white status text by default.
+- Feature: Add a Text color picker to the modern folder editor Status section so status labels can be customized separately from Started, Paused, and Stopped indicators.
+- Compatibility: Apply the independent status text color across Docker folders and previews, VM folders, Dashboard folders, inherited defaults, starter templates, and existing folders without requiring migration.
+- Diagnostics: Include the normalized status text color in privacy-safe folder integrity diagnostics.
+- Localization: Translate the new status text control and updated guidance across all 27 supported locales.
+- Test: Cover status text normalization, editor persistence, runtime theme separation, localization, browser fixtures, responsive themes, and large-runtime performance budgets.
+
+
+###2026.08.27.01
+- Fix: Use green as the default started-status color in the folder editor and across Docker, VM, and Dashboard folder surfaces.
+- Compatibility: Migrate the previous unmodified white started-status default to green while preserving white when it is explicitly selected and saved in the modern folder editor.
+- Fix: Show a visible pending spinner on compact multi-row Docker previews while a container lifecycle action, including Restart, is in progress.
+- Fix: Keep Restart pending through the initial already-running snapshot, then restore the container preview to its current started, paused, or stopped state after reconciliation.
+- Diagnostics: Record privacy-safe counts for lifecycle preview surfaces and pending or restored status icons without exporting container identities.
+- Test: Cover legacy and explicit status-color behavior, compact-preview lifecycle feedback, restart timing, browser fixtures, responsive themes, and large-runtime performance budgets.
+
+
+###2026.08.25.01
+- Fix: Open the same child-folder action menu from a collapsed parent preview when using a normal click, right-click, touch activation, Enter, or Space.
+- Accessibility: Position keyboard-opened menus beside the child-folder preview chip, focus the first menu action, and retain the existing Expand, Edit, and Open folder actions.
+- Diagnostics: Add bounded child-folder preview render, binding, menu-attempt, success, failure, and input-method counters to sanitized support bundles.
+- Privacy: Keep child-folder preview diagnostics free of folder names, identifiers, selectors, URLs, and pointer coordinates.
+- Test: Cover child-folder menu activation after repeated multi-row preview reconciliation and verify mouse, keyboard, context-menu, touch, focus, geometry, and diagnostic behavior in the browser fixture suite.
###2026.08.24.03
diff --git a/folderview.plus.xml b/folderview.plus.xml
index c677cd37..5a7ada84 100644
--- a/folderview.plus.xml
+++ b/folderview.plus.xml
@@ -10,7 +10,7 @@
FolderView Plus organizes Docker, VM, and Dashboard views into folders in Unraid, with starter setup tools, rules, bulk assignment, recovery, templates, and diagnostics.
-2026-08-24
+2026-08-28
7.0.0
folder view docker vm dashboard organization groups sorting import export backup recovery rules templates diagnostics
https://forums.unraid.net/topic/197631-plugin-folderview-plus/
diff --git a/package-lock.json b/package-lock.json
index a600d7aa..ea953161 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
"devDependencies": {
"axe-core": "4.13.0",
"c8": "12.0.0",
+ "fast-check": "4.9.0",
"jquery": "3.7.1",
"opencc-js": "1.4.1",
"playwright": "1.62.1"
@@ -215,6 +216,29 @@
"node": ">=6"
}
},
+ "node_modules/fast-check": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.9.0.tgz",
+ "integrity": "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "pure-rand": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=12.17.0"
+ }
+ },
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@@ -551,6 +575,23 @@
"node": ">=20"
}
},
+ "node_modules/pure-rand": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz",
+ "integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/dubzzz"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fast-check"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/semver": {
"version": "7.8.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
diff --git a/package.json b/package.json
index a736e657..47883889 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"devDependencies": {
"axe-core": "4.13.0",
"c8": "12.0.0",
+ "fast-check": "4.9.0",
"jquery": "3.7.1",
"opencc-js": "1.4.1",
"playwright": "1.62.1"
diff --git a/scripts/classify_ci_changes.mjs b/scripts/classify_ci_changes.mjs
index 6997f986..dfe082aa 100644
--- a/scripts/classify_ci_changes.mjs
+++ b/scripts/classify_ci_changes.mjs
@@ -27,8 +27,10 @@ export const FILTERS = Object.freeze({
workflows: [
'.github/workflows/**',
'.github/actions/**',
+ '.github/ISSUE_TEMPLATE/**',
'scripts/classify_ci_changes.mjs',
'scripts/actionlint_guard.sh',
+ 'scripts/issue_form_guard.mjs',
'scripts/run_ci_suite.sh',
'scripts/test_runner_contract_guard.mjs',
'scripts/build_release_notes.sh',
diff --git a/scripts/csp_readiness_guard.mjs b/scripts/csp_readiness_guard.mjs
index 76365521..c89d6b8f 100644
--- a/scripts/csp_readiness_guard.mjs
+++ b/scripts/csp_readiness_guard.mjs
@@ -45,13 +45,13 @@ const patterns = {
};
const lineNumberAt = (source, index) => source.slice(0, index).split('\n').length;
const explicitHtmlSinkReviews = new Map([
- ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js:1294', 'Port endpoints and protocols are escaped by buildDockerPortEndpoint before the markup builder returns.'],
- ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js:3395', 'The loading overlay contains plugin-authored static markup only.'],
- ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js:274', 'Action, label, icon, title, and menu values are escaped by the local markup builders.'],
+ ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js:1313', 'Port endpoints and protocols are escaped by buildDockerPortEndpoint before the markup builder returns.'],
+ ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js:3443', 'The loading overlay contains plugin-authored static markup only.'],
+ ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js:312', 'Action, label, icon, title, and menu values are escaped by the local markup builders.'],
['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.command-view.js:504', 'Names, identifiers, states, actions, and error text are escaped; counts are normalized numbers and image sources are sanitized.'],
['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-controls.js:221', 'The stable-toggle controller accepts only plugin-owned markup builders and validates the expected input after mounting.'],
- ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:471', 'buildTopChrome returns plugin-authored editor chrome with no persisted-value interpolation.'],
- ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:485', 'The action bar shell contains plugin-authored static markup only.'],
+ ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:470', 'buildTopChrome returns plugin-authored editor chrome with no persisted-value interpolation.'],
+ ['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:484', 'The action bar shell contains plugin-authored static markup only.'],
['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:604', 'Panel titles and descriptions come from the frozen plugin-authored section metadata table.'],
['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:680', 'Section keys and labels come from the plugin-authored editor metadata tables.'],
['src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.chrome.js:832', 'The preview image source is a fixed plugin asset path.'],
diff --git a/scripts/fixture_browser_tests.mjs b/scripts/fixture_browser_tests.mjs
index 17a191a0..976ccdea 100644
--- a/scripts/fixture_browser_tests.mjs
+++ b/scripts/fixture_browser_tests.mjs
@@ -9,11 +9,12 @@ import { registerFoundationFixtureCases } from '../tests/browser/cases/foundatio
import { registerDockerFixtureCases } from '../tests/browser/cases/docker.mjs';
import { registerDashboardLifecycleFixtureCases } from '../tests/browser/cases/dashboard-lifecycle.mjs';
import { registerRuntimeInteractionsFixtureCases } from '../tests/browser/cases/runtime-interactions.mjs';
+import { registerDockerHiddenFolderFixtureCases } from '../tests/browser/cases/docker-hidden-folders.mjs';
import { registerSettingsFixtureCases } from '../tests/browser/cases/settings.mjs';
import { registerSettingsStartOrderFixtureCases } from '../tests/browser/cases/settings-start-order.mjs';
import { registerFolderEditorFixtureCases } from '../tests/browser/cases/folder-editor.mjs';
+import { registerFolderWebuiProfileFixtureCases } from '../tests/browser/cases/folder-webui-profiles.mjs'; import { registerFolderWebuiProfileThemeFixtureCase } from '../tests/browser/cases/folder-webui-profile-theme.mjs';
import { registerImportFixtureCases } from '../tests/browser/cases/import.mjs';
-
const scriptPath = fileURLToPath(import.meta.url);
const rootDir = path.resolve(path.dirname(scriptPath), '..');
const pluginDir = path.join(rootDir, 'src', 'folderview.plus', 'usr', 'local', 'emhttp', 'plugins', 'folderview.plus');
@@ -45,9 +46,10 @@ registerFoundationFixtureCases(caseContext);
registerDockerFixtureCases(caseContext);
registerDashboardLifecycleFixtureCases(caseContext);
registerRuntimeInteractionsFixtureCases(caseContext);
+registerDockerHiddenFolderFixtureCases(caseContext);
registerSettingsFixtureCases(caseContext);
registerSettingsStartOrderFixtureCases(caseContext);
-registerFolderEditorFixtureCases(caseContext);
+registerFolderEditorFixtureCases(caseContext); registerFolderWebuiProfileFixtureCases(caseContext); registerFolderWebuiProfileThemeFixtureCase(caseContext);
registerImportFixtureCases(caseContext);
await runFixtureBrowserSuite({
diff --git a/scripts/install_smoke.sh b/scripts/install_smoke.sh
index 559764b5..ad09a976 100644
--- a/scripts/install_smoke.sh
+++ b/scripts/install_smoke.sh
@@ -86,6 +86,7 @@ REQUIRED_ARCHIVE_ENTRIES=(
"./usr/local/emhttp/plugins/folderview.plus/scripts/archive_preflight.sh"
"./usr/local/emhttp/plugins/folderview.plus/scripts/install_icon_asset_pack.sh"
"./usr/local/emhttp/plugins/folderview.plus/scripts/install_report.sh"
+ "./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.column-controller.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js"
@@ -209,6 +210,7 @@ REQUIRED_FILES=(
"scripts/archive_preflight.sh"
"scripts/install_icon_asset_pack.sh"
"scripts/install_report.sh"
+ "scripts/docker.runtime.child-folder-preview-menu.js"
"scripts/docker.runtime.hierarchy.js"
"scripts/docker.runtime.column-controller.js"
"scripts/docker.runtime.actions.js"
diff --git a/scripts/issue_form_guard.mjs b/scripts/issue_form_guard.mjs
new file mode 100644
index 00000000..b21ea671
--- /dev/null
+++ b/scripts/issue_form_guard.mjs
@@ -0,0 +1,223 @@
+#!/usr/bin/env node
+import fs from 'node:fs';
+import path from 'node:path';
+
+const repoRoot = path.resolve(process.cwd());
+const failures = [];
+
+const fail = (message) => failures.push(message);
+const read = (relativePath) => fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
+const formPaths = Object.freeze([
+ '.github/ISSUE_TEMPLATE/bug_report.yml',
+ '.github/ISSUE_TEMPLATE/feature_request.yml',
+ '.github/ISSUE_TEMPLATE/support_troubleshooting.yml',
+ '.github/ISSUE_TEMPLATE/translation.yml'
+]);
+
+const findBodyBlock = (source, id) => {
+ const lines = source.split(/\r?\n/);
+ const idIndex = lines.findIndex((line) => line === ` id: ${id}`);
+ if (idIndex < 0) return '';
+ let start = idIndex;
+ while (start >= 0 && !lines[start].startsWith(' - type:')) start -= 1;
+ let end = idIndex + 1;
+ while (end < lines.length && !lines[end].startsWith(' - type:')) end += 1;
+ return lines.slice(Math.max(0, start), end).join('\n');
+};
+
+const extractOptions = (source, id) => {
+ const block = findBodyBlock(source, id);
+ if (!block) return [];
+ const lines = block.split(/\r?\n/);
+ const optionsIndex = lines.findIndex((line) => line === ' options:');
+ if (optionsIndex < 0) return [];
+ return lines.slice(optionsIndex + 1)
+ .filter((line) => line.startsWith(' - '))
+ .map((line) => line.slice(' - '.length).trim());
+};
+
+const requireText = (source, text, label) => {
+ if (!source.includes(text)) fail(`${label} is missing required text: ${text}`);
+};
+
+const requireIds = (source, relativePath, ids) => {
+ for (const id of ids) {
+ const block = findBodyBlock(source, id);
+ if (!block) {
+ fail(`${relativePath} is missing required field id: ${id}`);
+ continue;
+ }
+ if (id !== 'relevant_settings' && !/\n validations:\n required: true(?:\n|$)/.test(block)) {
+ fail(`${relativePath} field ${id} must be required.`);
+ }
+ }
+};
+
+const requireOptions = (source, relativePath, id, expectedOptions) => {
+ const options = extractOptions(source, id);
+ for (const expected of expectedOptions) {
+ if (!options.includes(expected)) {
+ fail(`${relativePath} field ${id} is missing option: ${expected}`);
+ }
+ }
+};
+
+const requireRequiredCheckbox = (source, relativePath, id) => {
+ const block = findBodyBlock(source, id);
+ if (!block) {
+ fail(`${relativePath} is missing required checkbox field id: ${id}`);
+ } else if (!/\n required: true(?:\n|$)/.test(block)) {
+ fail(`${relativePath} checkbox field ${id} must contain a required confirmation.`);
+ }
+};
+
+for (const relativePath of formPaths) {
+ const fullPath = path.join(repoRoot, relativePath);
+ if (!fs.existsSync(fullPath)) {
+ fail(`Required issue form is missing: ${relativePath}`);
+ continue;
+ }
+ const source = fs.readFileSync(fullPath, 'utf8');
+ const ids = [...source.matchAll(/^ id: ([A-Za-z][A-Za-z0-9_-]*)\s*$/gm)].map((match) => match[1]);
+ const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
+ if (duplicates.length) {
+ fail(`${relativePath} contains duplicate field ids: ${[...new Set(duplicates)].join(', ')}`);
+ }
+ for (const key of ['name:', 'description:', 'title:', 'labels:', 'body:']) {
+ if (!source.includes(key)) fail(`${relativePath} is missing top-level key: ${key}`);
+ }
+}
+
+const bugPath = '.github/ISSUE_TEMPLATE/bug_report.yml';
+const bug = read(bugPath);
+requireIds(bug, bugPath, [
+ 'unraid_version', 'plugin_version', 'release_channel', 'browser_device', 'install_method',
+ 'access_method', 'docker_view_mode', 'theme_context', 'conflict_plugins', 'configuration_origin',
+ 'affected_area', 'reproducibility', 'steps_to_reproduce', 'expected_behavior', 'actual_behavior',
+ 'relevant_settings', 'support_bundle_status'
+]);
+requireOptions(bug, bugPath, 'affected_area', [
+ 'Docker container actions / context menus',
+ 'Docker API / provider compatibility',
+ 'Unraid native Docker / prerelease compatibility',
+ 'Reverse proxy / request guard',
+ 'Diagnostics / support bundles',
+ 'Docker Start Order',
+ 'Privacy / security behavior'
+]);
+requireOptions(bug, bugPath, 'release_channel', ['Stable / main', 'Dev testing build', 'Commit-specific build']);
+requireOptions(bug, bugPath, 'access_method', ['Direct LAN IP', 'Reverse proxy or custom domain', 'Unraid Connect']);
+requireOptions(bug, bugPath, 'docker_view_mode', ['Basic View', 'Advanced View', 'Tested in both', 'Not applicable']);
+requireOptions(bug, bugPath, 'configuration_origin', ['Fresh FolderView Plus configuration', 'Imported from FolderView3', 'Imported from FolderView2', 'Restored from a backup or export']);
+requireOptions(bug, bugPath, 'support_bundle_status', ['Attached below', 'Unable to export because Settings or Diagnostics will not load', 'Unable to export because installation failed', 'Need instructions']);
+requireRequiredCheckbox(bug, bugPath, 'privacy_confirmation');
+for (const text of [
+ 'latest stable release',
+ 'Advanced View',
+ 'Reverse proxy or custom domain',
+ 'Imported from FolderView3',
+ 'Sanitized support bundle status',
+ 'full/unsanitized bundle',
+ 'raw configuration export'
+]) requireText(bug, text, bugPath);
+if (bug.includes('main or latest commit URL')) {
+ fail(`${bugPath} must not use the ambiguous latest-commit pre-check wording.`);
+}
+
+const supportPath = '.github/ISSUE_TEMPLATE/support_troubleshooting.yml';
+const support = read(supportPath);
+requireIds(support, supportPath, [
+ 'help_topic', 'unraid_version', 'plugin_version', 'release_channel', 'install_source',
+ 'browser_device', 'access_method', 'docker_view_mode', 'theme_context', 'conflict_plugins',
+ 'configuration_origin', 'current_behavior', 'expected_behavior', 'troubleshooting_tried',
+ 'relevant_settings', 'support_bundle_status'
+]);
+requireOptions(support, supportPath, 'help_topic', [
+ 'Docker or VM actions / context menus',
+ 'Reverse proxy or custom hostname',
+ 'Diagnostics or support-bundle export',
+ 'Docker Start Order',
+ 'Unraid compatibility',
+ 'Performance or refresh behavior',
+ 'Privacy or security behavior'
+]);
+requireOptions(support, supportPath, 'release_channel', ['Stable / main', 'Dev testing build', 'Commit-specific build']);
+requireOptions(support, supportPath, 'access_method', ['Direct LAN IP', 'Reverse proxy or custom domain', 'Unraid Connect']);
+requireOptions(support, supportPath, 'docker_view_mode', ['Basic View', 'Advanced View', 'Tested in both', 'Not applicable']);
+requireOptions(support, supportPath, 'configuration_origin', ['Fresh FolderView Plus configuration', 'Imported from FolderView3', 'Imported from FolderView2', 'Restored from a backup or export']);
+requireOptions(support, supportPath, 'support_bundle_status', ['Attached below', 'Unable to export because Settings or Diagnostics will not load', 'Unable to export because installation failed', 'Need instructions']);
+requireRequiredCheckbox(support, supportPath, 'checklist');
+for (const text of [
+ 'Advanced View',
+ 'Reverse proxy or custom domain',
+ 'Imported from FolderView3',
+ 'Sanitized support bundle status',
+ 'full/unsanitized bundle',
+ 'raw configuration export',
+ 'I reviewed the Wiki and troubleshooting guidance'
+]) requireText(support, text, supportPath);
+
+const featurePath = '.github/ISSUE_TEMPLATE/feature_request.yml';
+const feature = read(featurePath);
+requireOptions(feature, featurePath, 'area', [
+ 'Docker container actions / context menus',
+ 'Docker API / provider compatibility',
+ 'Unraid native Docker / prerelease compatibility',
+ 'Docker Start Order',
+ 'Diagnostics / support bundles',
+ 'Reverse proxy / access paths',
+ 'Privacy / security behavior'
+]);
+
+const translationPath = '.github/ISSUE_TEMPLATE/translation.yml';
+const translation = read(translationPath);
+const localeDirectory = path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs');
+const expectedLocales = fs.readdirSync(localeDirectory)
+ .filter((name) => name.endsWith('.json'))
+ .map((name) => path.basename(name, '.json'))
+ .filter((locale) => !['en', 'extraction-report'].includes(locale))
+ .sort();
+const languageOptions = extractOptions(translation, 'language');
+const optionLocales = languageOptions
+ .map((option) => option.match(/\(([A-Za-z]{2,3}(?:-[A-Za-z0-9]+)*)\)$/)?.[1] || '')
+ .filter(Boolean)
+ .sort();
+const missingLocales = expectedLocales.filter((locale) => !optionLocales.includes(locale));
+const extraLocales = optionLocales.filter((locale) => !expectedLocales.includes(locale));
+const duplicateLocales = optionLocales.filter((locale, index) => optionLocales.indexOf(locale) !== index);
+if (missingLocales.length) fail(`${translationPath} is missing shipped locale options: ${missingLocales.join(', ')}`);
+if (extraLocales.length) fail(`${translationPath} contains locale options without shipped catalogs: ${extraLocales.join(', ')}`);
+if (duplicateLocales.length) fail(`${translationPath} contains duplicate locale options: ${[...new Set(duplicateLocales)].join(', ')}`);
+if (!languageOptions.includes('Another locale or regional variant')) {
+ fail(`${translationPath} must preserve the new-locale contribution option.`);
+}
+requireText(translation, 'Translation-and-Accessibility', translationPath);
+
+const configPath = '.github/ISSUE_TEMPLATE/config.yml';
+const config = read(configPath);
+for (const text of [
+ 'blank_issues_enabled: false',
+ 'https://github.com/alexphillips-dev/FolderView-Plus/wiki',
+ 'https://github.com/alexphillips-dev/FolderView-Plus/wiki/Troubleshooting',
+ 'https://github.com/alexphillips-dev/FolderView-Plus/wiki/Activity-Diagnostics-and-Support',
+ 'https://github.com/alexphillips-dev/FolderView-Plus/security/policy'
+]) requireText(config, text, configPath);
+
+const supportGuidePath = '.github/SUPPORT.md';
+const supportGuide = read(supportGuidePath);
+for (const text of [
+ 'installed release channel',
+ 'access path',
+ 'Docker Basic/Advanced View status',
+ 'configuration origin',
+ 'relevant FolderView Plus settings',
+ 'sanitized support-bundle status',
+ 'raw configuration export publicly'
+]) requireText(supportGuide, text, supportGuidePath);
+
+if (failures.length) {
+ for (const message of failures) console.error(`ERROR: ${message}`);
+ process.exit(1);
+}
+
+console.log(`Issue form guard passed: ${formPaths.length} forms, ${expectedLocales.length} translated locales, and diagnostic/privacy contracts are synchronized.`);
diff --git a/scripts/lang_usage_guard.sh b/scripts/lang_usage_guard.sh
index 5faa652f..c2102f0e 100644
--- a/scripts/lang_usage_guard.sh
+++ b/scripts/lang_usage_guard.sh
@@ -174,6 +174,15 @@ for (const fullPath of sourceFiles.sort()) {
referencedKeys.get(key).push(`${relPath}:${line}`);
}
+ const qualifiedMenuLabelRegex = /\bgetDockerMenuLabel\(\s*['"]([^'"]+\.[^'"]+)['"]/g;
+ while ((match = qualifiedMenuLabelRegex.exec(source)) !== null) {
+ const key = match[1].trim();
+ if (!key) continue;
+ const line = lineNumberAt(source, match.index);
+ if (!referencedKeys.has(key)) referencedKeys.set(key, []);
+ referencedKeys.get(key).push(`${relPath}:${line}`);
+ }
+
const declaredApplicationKeyRegexes = [
/\bi18nKey\s*:\s*['"]([^'"]+)['"]/g,
/\b(?:labelKey|placeholderKey)\s*:\s*['"](editor\.rules\.templates\.[^'"]+)['"]/g
diff --git a/scripts/load_order_contracts.json b/scripts/load_order_contracts.json
index 6fdc1bf3..3ef732ac 100644
--- a/scripts/load_order_contracts.json
+++ b/scripts/load_order_contracts.json
@@ -102,6 +102,7 @@
"scripts/folderviewplus.request.js",
"scripts/runtime.performance-telemetry.js",
"scripts/folderviewplus.runtime-snapshot.js",
+ "scripts/folder.webui-profiles.js",
"scripts/folder.member-identity.js",
"scripts/runtime.privacy.js",
"scripts/runtime.transport.core.js",
@@ -138,6 +139,7 @@
"scripts/docker.modules.js",
"scripts/docker.runtime.info.js",
"scripts/docker.runtime.preview-actions.js",
+ "scripts/docker.runtime.child-folder-preview-menu.js",
"scripts/docker.runtime.hierarchy.js",
"scripts/docker.runtime.actions.js",
"scripts/docker.runtime.host-guards.js",
@@ -145,6 +147,7 @@
"scripts/docker.runtime.diagnostics.js",
"scripts/docker.runtime.reconcile.js",
"scripts/docker.runtime.command-view.js",
+ "scripts/docker.runtime.hidden-folders.js",
"scripts/docker.runtime.action-bar.js",
"scripts/docker.js",
"scripts/docker.page-bootstrap.js",
@@ -169,6 +172,7 @@
"scripts/folderviewplus.request.js",
"scripts/runtime.performance-telemetry.js",
"scripts/folderviewplus.runtime-snapshot.js",
+ "scripts/folder.webui-profiles.js",
"scripts/folder.member-identity.js",
"scripts/runtime.privacy.js",
"scripts/runtime.transport.core.js",
@@ -217,6 +221,7 @@
"scripts/folderviewplus.request.js",
"scripts/runtime.performance-telemetry.js",
"scripts/folderviewplus.runtime-snapshot.js",
+ "scripts/folder.webui-profiles.js",
"scripts/folder.member-identity.js",
"scripts/runtime.privacy.js",
"scripts/runtime.transport.core.js",
@@ -274,6 +279,7 @@
"scripts/folderviewplus.ui.js",
"scripts/folderviewplus.request.js",
"scripts/runtime.performance-telemetry.js",
+ "scripts/folder.webui-profiles.js",
"scripts/folder.member-identity.js",
"scripts/folderviewplus.prefs-store.js",
"scripts/folderviewplus.theme-surface.js",
diff --git a/scripts/release_guard.sh b/scripts/release_guard.sh
index 43dc1a50..a36bf6ff 100644
--- a/scripts/release_guard.sh
+++ b/scripts/release_guard.sh
@@ -202,6 +202,7 @@ if [[ -z "${ARCHIVE_SIZE_BYTES}" || "${ARCHIVE_SIZE_BYTES}" -gt "${MAX_ARCHIVE_B
fi
SOURCE_FOLDER_JS="${ROOT_DIR}/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js"
+SOURCE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS="${ROOT_DIR}/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js"
SOURCE_DOCKER_RUNTIME_HIERARCHY_JS="${ROOT_DIR}/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js"
SOURCE_DOCKER_RUNTIME_ACTIONS_JS="${ROOT_DIR}/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js"
SOURCE_FOLDER_SETTINGS_TRANSFER_JS="${ROOT_DIR}/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js"
@@ -241,6 +242,11 @@ if [[ ! -f "${SOURCE_FOLDER_JS}" ]]; then
echo "ERROR: Missing source folder editor script: ${SOURCE_FOLDER_JS}" >&2
exit 1
fi
+if [[ ! -f "${SOURCE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}" ]]; then
+ echo "ERROR: Missing source child-folder preview menu helper script: ${SOURCE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}" >&2
+ exit 1
+fi
+
if [[ ! -f "${SOURCE_DOCKER_RUNTIME_HIERARCHY_JS}" ]]; then
echo "ERROR: Missing source Docker hierarchy helper script: ${SOURCE_DOCKER_RUNTIME_HIERARCHY_JS}" >&2
exit 1
@@ -429,6 +435,7 @@ REQUIRED_ARCHIVE_PATHS=(
"./usr/local/emhttp/plugins/folderview.plus/scripts/install_report.sh"
"./usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.csp-events.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/folder.js"
+ "./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js"
"./usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js"
@@ -734,6 +741,7 @@ if ! grep -Eq "${current_version_notes_pattern}" "${RELEASE_NOTES_LIB}"; then
fi
TMP_ARCHIVE_FOLDER_JS="$(mktemp)"
+TMP_ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS="$(mktemp)"
TMP_ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS="$(mktemp)"
TMP_ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS="$(mktemp)"
TMP_ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS="$(mktemp)"
@@ -768,8 +776,9 @@ TMP_ARCHIVE_SERVER_LIB="$(mktemp)"
TMP_ARCHIVE_SERVER_LIB_DIAGNOSTICS="$(mktemp)"
TMP_ARCHIVE_SERVER_APPLY_FOLDER_SETTINGS="$(mktemp)"
TMP_ARCHIVE_SERVER_UPDATE_NOTES="$(mktemp)"
-trap 'rm -f "${TMP_ARCHIVE_FOLDER_JS}" "${TMP_ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS}" "${TMP_ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS}" "${TMP_ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS}" "${TMP_ARCHIVE_FOLDER_CSS}" "${TMP_ARCHIVE_SETTINGS_JS}" "${TMP_ARCHIVE_SETTINGS_DIRTY_JS}" "${TMP_ARCHIVE_SETTINGS_RUNTIME_PARITY_JS}" "${TMP_ARCHIVE_SETTINGS_SECTIONS_JS}" "${TMP_ARCHIVE_SETTINGS_SETUP_ASSISTANT_JS}" "${TMP_ARCHIVE_SETTINGS_SMART_DETECT_CONFIG_JS}" "${TMP_ARCHIVE_SETTINGS_STARTER_TEMPLATES_JS}" "${TMP_ARCHIVE_SETTINGS_SUPPORT_BUNDLE_PREVIEW_JS}" "${TMP_ARCHIVE_SETTINGS_DOWNLOAD_DIAGNOSTICS_JS}" "${TMP_ARCHIVE_SETTINGS_SUPPORT_BUNDLE_TELEMETRY_JS}" "${TMP_ARCHIVE_SETTINGS_ACTIVITY_DIAGNOSTICS_JS}" "${TMP_ARCHIVE_SETTINGS_TREE_JS}" "${TMP_ARCHIVE_SETTINGS_TREE_INTEGRITY_JS}" "${TMP_ARCHIVE_SETTINGS_MOBILE_REORDER_JS}" "${TMP_ARCHIVE_SETTINGS_FOLDER_EDITOR_JS}" "${TMP_ARCHIVE_SETTINGS_HEALTH_JS}" "${TMP_ARCHIVE_SETTINGS_WORKSPACES_JS}" "${TMP_ARCHIVE_SETTINGS_BULK_ASSIGNMENT_SHARED_JS}" "${TMP_ARCHIVE_SETTINGS_BULK_ASSIGNMENT_JS}" "${TMP_ARCHIVE_SETTINGS_RUNTIME_ACTIONS_JS}" "${TMP_ARCHIVE_SETTINGS_WIZARD_JS}" "${TMP_ARCHIVE_SETTINGS_IMPORT_JS}" "${TMP_ARCHIVE_SETTINGS_CSS}" "${TMP_ARCHIVE_SETTINGS_DOWNLOAD_DIAGNOSTICS_CSS}" "${TMP_ARCHIVE_FOLDER_PAGE}" "${TMP_ARCHIVE_SETTINGS_PAGE}" "${TMP_ARCHIVE_SERVER_LIB}" "${TMP_ARCHIVE_SERVER_LIB_DIAGNOSTICS}" "${TMP_ARCHIVE_SERVER_APPLY_FOLDER_SETTINGS}" "${TMP_ARCHIVE_SERVER_UPDATE_NOTES}"' EXIT
+trap 'rm -f "${TMP_ARCHIVE_FOLDER_JS}" "${TMP_ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}" "${TMP_ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS}" "${TMP_ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS}" "${TMP_ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS}" "${TMP_ARCHIVE_FOLDER_CSS}" "${TMP_ARCHIVE_SETTINGS_JS}" "${TMP_ARCHIVE_SETTINGS_DIRTY_JS}" "${TMP_ARCHIVE_SETTINGS_RUNTIME_PARITY_JS}" "${TMP_ARCHIVE_SETTINGS_SECTIONS_JS}" "${TMP_ARCHIVE_SETTINGS_SETUP_ASSISTANT_JS}" "${TMP_ARCHIVE_SETTINGS_SMART_DETECT_CONFIG_JS}" "${TMP_ARCHIVE_SETTINGS_STARTER_TEMPLATES_JS}" "${TMP_ARCHIVE_SETTINGS_SUPPORT_BUNDLE_PREVIEW_JS}" "${TMP_ARCHIVE_SETTINGS_DOWNLOAD_DIAGNOSTICS_JS}" "${TMP_ARCHIVE_SETTINGS_SUPPORT_BUNDLE_TELEMETRY_JS}" "${TMP_ARCHIVE_SETTINGS_ACTIVITY_DIAGNOSTICS_JS}" "${TMP_ARCHIVE_SETTINGS_TREE_JS}" "${TMP_ARCHIVE_SETTINGS_TREE_INTEGRITY_JS}" "${TMP_ARCHIVE_SETTINGS_MOBILE_REORDER_JS}" "${TMP_ARCHIVE_SETTINGS_FOLDER_EDITOR_JS}" "${TMP_ARCHIVE_SETTINGS_HEALTH_JS}" "${TMP_ARCHIVE_SETTINGS_WORKSPACES_JS}" "${TMP_ARCHIVE_SETTINGS_BULK_ASSIGNMENT_SHARED_JS}" "${TMP_ARCHIVE_SETTINGS_BULK_ASSIGNMENT_JS}" "${TMP_ARCHIVE_SETTINGS_RUNTIME_ACTIONS_JS}" "${TMP_ARCHIVE_SETTINGS_WIZARD_JS}" "${TMP_ARCHIVE_SETTINGS_IMPORT_JS}" "${TMP_ARCHIVE_SETTINGS_CSS}" "${TMP_ARCHIVE_SETTINGS_DOWNLOAD_DIAGNOSTICS_CSS}" "${TMP_ARCHIVE_FOLDER_PAGE}" "${TMP_ARCHIVE_SETTINGS_PAGE}" "${TMP_ARCHIVE_SERVER_LIB}" "${TMP_ARCHIVE_SERVER_LIB_DIAGNOSTICS}" "${TMP_ARCHIVE_SERVER_APPLY_FOLDER_SETTINGS}" "${TMP_ARCHIVE_SERVER_UPDATE_NOTES}"' EXIT
ARCHIVE_FOLDER_JS_PATH="./usr/local/emhttp/plugins/folderview.plus/scripts/folder.js"
+ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS_PATH="./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js"
ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS_PATH="./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js"
ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS_PATH="./usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js"
ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS_PATH="./usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js"
@@ -807,6 +816,9 @@ ARCHIVE_SERVER_UPDATE_NOTES_PATH="./usr/local/emhttp/plugins/folderview.plus/ser
if ! grep -Fxq "${ARCHIVE_FOLDER_JS_PATH}" <<< "${ARCHIVE_LIST}"; then
ARCHIVE_FOLDER_JS_PATH="${ARCHIVE_FOLDER_JS_PATH#./}"
fi
+if ! grep -Fxq "${ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS_PATH}" <<< "${ARCHIVE_LIST}"; then
+ ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS_PATH="${ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS_PATH#./}"
+fi
if ! grep -Fxq "${ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS_PATH}" <<< "${ARCHIVE_LIST}"; then
ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS_PATH="${ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS_PATH#./}"
fi
@@ -910,6 +922,7 @@ if ! grep -Fxq "${ARCHIVE_SERVER_UPDATE_NOTES_PATH}" <<< "${ARCHIVE_LIST}"; then
ARCHIVE_SERVER_UPDATE_NOTES_PATH="${ARCHIVE_SERVER_UPDATE_NOTES_PATH#./}"
fi
tar -xOf "${ARCHIVE_FILE}" "${ARCHIVE_FOLDER_JS_PATH}" > "${TMP_ARCHIVE_FOLDER_JS}"
+tar -xOf "${ARCHIVE_FILE}" "${ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS_PATH}" > "${TMP_ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}"
tar -xOf "${ARCHIVE_FILE}" "${ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS_PATH}" > "${TMP_ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS}"
tar -xOf "${ARCHIVE_FILE}" "${ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS_PATH}" > "${TMP_ARCHIVE_DOCKER_RUNTIME_ACTIONS_JS}"
tar -xOf "${ARCHIVE_FILE}" "${ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS_PATH}" > "${TMP_ARCHIVE_FOLDER_SETTINGS_TRANSFER_JS}"
@@ -950,6 +963,10 @@ if ! env_truthy "${FVPLUS_ALLOW_PACKAGED_SOURCE_DRIFT:-0}"; then
fail_packaged_source_mismatch "Packaged folder.js does not match source folder.js."
fi
+ if ! text_files_match "${SOURCE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}" "${TMP_ARCHIVE_DOCKER_CHILD_FOLDER_PREVIEW_MENU_JS}"; then
+ fail_packaged_source_mismatch "Packaged docker.runtime.child-folder-preview-menu.js does not match source docker.runtime.child-folder-preview-menu.js."
+ fi
+
if ! text_files_match "${SOURCE_DOCKER_RUNTIME_HIERARCHY_JS}" "${TMP_ARCHIVE_DOCKER_RUNTIME_HIERARCHY_JS}"; then
fail_packaged_source_mismatch "Packaged docker.runtime.hierarchy.js does not match source docker.runtime.hierarchy.js."
fi
diff --git a/scripts/run_ci_suite.sh b/scripts/run_ci_suite.sh
index 75df2c96..f8bc7a31 100644
--- a/scripts/run_ci_suite.sh
+++ b/scripts/run_ci_suite.sh
@@ -249,6 +249,7 @@ run_lane() {
;;
workflow-guards)
run_timed_step actionlint bash scripts/actionlint_guard.sh
+ run_timed_step issue-form-contract "${NODE_BIN}" "$(fvplus::path_for_command "${NODE_BIN}" "scripts/issue_form_guard.mjs")"
run_timed_step docs-metadata bash scripts/docs_metadata_guard.sh
run_timed_step release-notes-consistency bash scripts/release_notes_consistency_guard.sh
run_timed_step workflow-self-check bash scripts/workflow_self_check.sh
@@ -275,6 +276,7 @@ run_lane() {
run_timed_step repro-build bash scripts/repro_build_guard.sh
run_timed_step docs-metadata bash scripts/docs_metadata_guard.sh
run_timed_step release-notes-consistency bash scripts/release_notes_consistency_guard.sh
+ run_timed_step issue-form-contract "${NODE_BIN}" "$(fvplus::path_for_command "${NODE_BIN}" "scripts/issue_form_guard.mjs")"
run_timed_step workflow-self-check bash scripts/workflow_self_check.sh
;;
fixture-browser)
diff --git a/scripts/test_runner_contracts.json b/scripts/test_runner_contracts.json
index 6d0d9689..1cd3eafe 100644
--- a/scripts/test_runner_contracts.json
+++ b/scripts/test_runner_contracts.json
@@ -13,9 +13,12 @@
"../tests/browser/cases/docker.mjs",
"../tests/browser/cases/dashboard-lifecycle.mjs",
"../tests/browser/cases/runtime-interactions.mjs",
+ "../tests/browser/cases/docker-hidden-folders.mjs",
"../tests/browser/cases/settings.mjs",
"../tests/browser/cases/settings-start-order.mjs",
"../tests/browser/cases/folder-editor.mjs",
+ "../tests/browser/cases/folder-webui-profiles.mjs",
+ "../tests/browser/cases/folder-webui-profile-theme.mjs",
"../tests/browser/cases/import.mjs"
]
}
@@ -28,9 +31,13 @@
{ "file": "tests/browser/cases/docker.mjs", "limit": 220, "owner": "fixture Docker cases" },
{ "file": "tests/browser/cases/dashboard-lifecycle.mjs", "limit": 365, "owner": "fixture Dashboard and VM lifecycle cases" },
{ "file": "tests/browser/cases/runtime-interactions.mjs", "limit": 155, "owner": "fixture runtime interaction cases" },
+ { "file": "tests/browser/cases/docker-hidden-folders.mjs", "limit": 27, "owner": "fixture hidden-folder recovery cases" },
+ { "file": "tests/browser/helpers/docker-hidden-folder-context.mjs", "limit": 32, "owner": "fixture hidden-folder timing and theme assertions" },
{ "file": "tests/browser/cases/settings.mjs", "limit": 610, "owner": "fixture Settings cases" },
{ "file": "tests/browser/cases/settings-start-order.mjs", "limit": 48, "owner": "fixture Docker start-order Settings cases" },
{ "file": "tests/browser/cases/folder-editor.mjs", "limit": 44, "owner": "fixture Folder Editor cases" },
+ { "file": "tests/browser/cases/folder-webui-profiles.mjs", "limit": 32, "owner": "fixture Folder WebUI profile cases" },
+ { "file": "tests/browser/cases/folder-webui-profile-theme.mjs", "limit": 42, "owner": "fixture Folder WebUI profile theme cases" },
{ "file": "tests/browser/cases/import.mjs", "limit": 74, "owner": "fixture import and export cases" }
],
"intent": {
@@ -40,21 +47,25 @@
"tests/browser/cases/docker.mjs",
"tests/browser/cases/dashboard-lifecycle.mjs",
"tests/browser/cases/runtime-interactions.mjs",
+ "tests/browser/cases/docker-hidden-folders.mjs",
"tests/browser/cases/settings.mjs",
"tests/browser/cases/settings-start-order.mjs",
"tests/browser/cases/folder-editor.mjs",
+ "tests/browser/cases/folder-webui-profiles.mjs",
+ "tests/browser/cases/folder-webui-profile-theme.mjs",
"tests/browser/cases/import.mjs"
],
"supportFiles": [
"scripts/fixture_browser_tests.mjs",
"scripts/lib/fixture-browser-server.mjs",
"scripts/lib/fixture-browser-runner.mjs",
- "tests/browser/helpers/docker-preview-context.mjs"
+ "tests/browser/helpers/docker-preview-context.mjs",
+ "tests/browser/helpers/docker-hidden-folder-context.mjs"
],
- "testCount": 43,
- "assertionCount": 557,
- "pageEvaluateCount": 109,
- "orderedTitleSha256": "491369c2cae7533952fe8634ccef5185f1a713d57d2b440273973accb8d212aa"
+ "testCount": 49,
+ "assertionCount": 617,
+ "pageEvaluateCount": 124,
+ "orderedTitleSha256": "8d5719c0e640edebabad9e8721447f53f85350e8555b26a4e890cfdab1a5a804"
}
}
}
diff --git a/scripts/workflow_self_check.sh b/scripts/workflow_self_check.sh
index 51e50d5f..c0334400 100644
--- a/scripts/workflow_self_check.sh
+++ b/scripts/workflow_self_check.sh
@@ -41,6 +41,7 @@ for (const relativePath of [
'scripts/run_ci_suite.sh',
'scripts/actionlint_guard.sh',
'scripts/classify_ci_changes.mjs',
+ 'scripts/issue_form_guard.mjs',
'scripts/csp_readiness_guard.mjs',
'scripts/fixture_browser_tests.sh',
'scripts/fixture_browser_tests.mjs',
@@ -88,6 +89,9 @@ if (!/detect-changes:/.test(ciWorkflow)) {
if (!/quality:/.test(ciWorkflow)) {
fail('CI workflow must define a quality summary job.');
}
+if (!/^ guard-suite:\s*$[\s\S]*?^ name:\s*CI tests and guards\s*$/m.test(ciWorkflow)) {
+ fail('CI guard-suite must expose an always-present test signal recognized by repository quality scanners.');
+}
if (/dorny\/paths-filter@/.test(ciWorkflow)) {
fail('CI workflow must use the repository-owned change classifier instead of dorny/paths-filter.');
}
@@ -119,6 +123,13 @@ if (!/runtime_performance_benchmarks\.sh/.test(read('scripts/run_ci_suite.sh')))
if (!/test_runner_contract_guard\.mjs/.test(read('scripts/run_ci_suite.sh'))) {
fail('The shared lint lane must enforce split test-runner contracts.');
}
+if (!/run_timed_step issue-form-contract/.test(read('scripts/run_ci_suite.sh'))) {
+ fail('Workflow and full guard lanes must enforce issue-form contracts.');
+}
+if (!/'\.github\/ISSUE_TEMPLATE\/\*\*'/.test(read('scripts/classify_ci_changes.mjs'))
+ || !/'scripts\/issue_form_guard\.mjs'/.test(read('scripts/classify_ci_changes.mjs'))) {
+ fail('Issue forms and their guard must be classified as workflow changes.');
+}
if (!/tmp\/fixture-browser-artifacts/.test(ciWorkflow)) {
fail('CI workflow must retain deterministic fixture browser artifacts.');
}
@@ -173,8 +184,9 @@ for (const [name, workflow] of [
if (!/bash scripts\/build_release_notes\.sh/.test(releaseOnMainWorkflow)) {
fail('Release On Main workflow must build release notes via scripts/build_release_notes.sh.');
}
-if (!/permissions:\s*\n\s*contents:\s*write\s*\n\s*id-token:\s*write\s*\n\s*attestations:\s*write/.test(releaseOnMainWorkflow)) {
- fail('Release On Main must grant only the release, OIDC, and attestation permissions required for signed provenance.');
+if (!/^permissions:\s*\n contents:\s*read\s*$/m.test(releaseOnMainWorkflow)
+ || !/permissions:\s*\n\s*contents:\s*write\s*\n\s*id-token:\s*write\s*\n\s*attestations:\s*write/.test(jobBlock(releaseOnMainWorkflow, 'release'))) {
+ fail('Release On Main must keep top-level access read-only and scope release, OIDC, and attestation writes to its release job.');
}
if ((codeqlWorkflow.match(/github\/codeql-action\/(?:init|autobuild|analyze)@[0-9a-f]{40}\s+# v4/g) || []).length !== 3) {
fail('CodeQL must use commit-pinned v4 init, autobuild, and analyze actions.');
@@ -185,6 +197,10 @@ if (!/node scripts\/codeql_alert_guard\.mjs --commit-sha/.test(codeqlWorkflow))
if (!/schedule:/.test(codeqlWorkflow) || !/workflow_dispatch:/.test(codeqlWorkflow)) {
fail('CodeQL must run on schedule and support manual recovery checks.');
}
+if (!/^permissions:\s*\n actions:\s*read\s*\n contents:\s*read\s*$/m.test(codeqlWorkflow)
+ || !/permissions:\s*\n\s*actions:\s*read\s*\n\s*contents:\s*read\s*\n\s*security-events:\s*write/.test(jobBlock(codeqlWorkflow, 'analyze'))) {
+ fail('CodeQL must keep top-level access read-only and scope security-events write to the analyze job.');
+}
if (!/actions\/dependency-review-action@[0-9a-f]{40}\s+# v5/.test(dependencyReviewWorkflow)
|| !/fail-on-severity:\s*high/.test(dependencyReviewWorkflow)
|| !/license-check:\s*true/.test(dependencyReviewWorkflow)
@@ -260,8 +276,9 @@ if (!/bash scripts\/prepare_backmerge_dev_package\.sh/.test(backmergeWorkflow) |
/FVPLUS_ALLOW_PACKAGED_SOURCE_DRIFT:\s*'1'/.test(backmergeWorkflow)) {
fail('Back-merge workflow must package merged source instead of bypassing packaged/source drift validation.');
}
-if (!/pull-requests:\s*write/.test(backmergeWorkflow)) {
- fail('Back-merge workflow must have pull-requests: write permission.');
+if (!/^permissions:\s*\n contents:\s*read\s*$/m.test(backmergeWorkflow)
+ || !/permissions:\s*\n\s*contents:\s*write\s*\n\s*pull-requests:\s*write/.test(jobBlock(backmergeWorkflow, 'backmerge'))) {
+ fail('Back-merge workflow must keep top-level access read-only and scope contents and pull-request writes to the backmerge job.');
}
if (!/Create or update back-merge PR/.test(backmergeWorkflow) ||
!/gh api --method POST/.test(backmergeWorkflow) ||
@@ -330,15 +347,20 @@ if (!/FVPLUS_FIXTURE_BROWSERS:\s*chromium,firefox,webkit/.test(scheduledValidati
if (/FVPLUS_UNRAID_MATRIX|FVPLUS_BROWSER_SMOKE_URL|FVPLUS_THEME_MATRIX_URLS|live-unraid:|gh issue/.test(scheduledValidationWorkflow)) {
fail('Scheduled validation must not depend on live-Unraid targets, secrets, or issue automation.');
}
+const cloneTrafficCollectJob = jobBlock(cloneTrafficBadgeWorkflow, 'collect');
+const cloneTrafficPublishJob = jobBlock(cloneTrafficBadgeWorkflow, 'publish');
if (!/schedule:/.test(cloneTrafficBadgeWorkflow)
|| !/workflow_dispatch:/.test(cloneTrafficBadgeWorkflow)
- || !/permissions:\s*\n\s*contents:\s*write/.test(cloneTrafficBadgeWorkflow)
- || !/secrets\.FVPLUS_TRAFFIC_TOKEN/.test(cloneTrafficBadgeWorkflow)
- || !/repos\/\$\{GITHUB_REPOSITORY\}\/traffic\/clones/.test(cloneTrafficBadgeWorkflow)
- || !/github\.token/.test(cloneTrafficBadgeWorkflow)
+ || !/^permissions:\s*\n contents:\s*read\s*$/m.test(cloneTrafficBadgeWorkflow)
+ || !/secrets\.FVPLUS_TRAFFIC_TOKEN/.test(cloneTrafficCollectJob)
+ || /github\.token/.test(cloneTrafficCollectJob)
+ || !/repos\/\$\{GITHUB_REPOSITORY\}\/traffic\/clones/.test(cloneTrafficCollectJob)
+ || !/permissions:\s*\n\s*contents:\s*write/.test(cloneTrafficPublishJob)
+ || !/github\.token/.test(cloneTrafficPublishJob)
+ || /secrets\.FVPLUS_TRAFFIC_TOKEN/.test(cloneTrafficPublishJob)
|| !/--branch metrics/.test(cloneTrafficBadgeWorkflow)
|| !/Total clones \\u00b7 14d/.test(cloneTrafficBadgeWorkflow)) {
- fail('Clone traffic badge workflow must publish the authenticated rolling 14-day total to the isolated metrics branch.');
+ fail('Clone traffic badge workflow must isolate authenticated collection from the write-scoped metrics publisher.');
}
if (!/schedule:/.test(scheduledWorkflowHealthWorkflow)
|| !/workflow_dispatch:/.test(scheduledWorkflowHealthWorkflow)
@@ -361,7 +383,7 @@ for (const [workflowName, workflow, jobNames] of [
['dependency-review', dependencyReviewWorkflow, ['dependency-review']],
['dependency-vulnerability-scan', dependencyVulnerabilityScanWorkflow, ['scan']],
['scorecard', scorecardWorkflow, ['analysis']],
- ['clone-traffic-badge', cloneTrafficBadgeWorkflow, ['refresh']],
+ ['clone-traffic-badge', cloneTrafficBadgeWorkflow, ['collect', 'publish']],
['scheduled-validation', scheduledValidationWorkflow, ['cross-browser-fixtures']],
['scheduled-workflow-health', scheduledWorkflowHealthWorkflow, ['watchdog']],
['unraid-compatibility-monitor', upstreamMonitorWorkflow, ['monitor', 'php-runtime-compatibility']]
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page
index 4a9a52d0..9ed132f3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/Folder.page
@@ -127,6 +127,7 @@ try {
+
@@ -898,6 +899,10 @@ try {
Stopped
+
+
+
+ - WebUI profiles:
+ -
+
+
+
+
+
+ Create reusable actions that open only the selected running containers. Current WebUI addresses are resolved when the profile is launched.
+
+
- Custom actions:
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Dashboard.page b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Dashboard.page
index a37daa16..e622c2c8 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Dashboard.page
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Dashboard.page
@@ -55,6 +55,7 @@ require_once('/usr/local/emhttp/plugins/folderview.plus/scripts/custom.php');
+
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Docker.page b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Docker.page
index 06dfec8e..a4ece0b6 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Docker.page
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.Docker.page
@@ -51,6 +51,7 @@ $fvplusRuntimePreflightHasFatal = runtimePreflightHasFatal($fvplusRuntimePreflig
+
@@ -87,6 +88,7 @@ $fvplusRuntimePreflightHasFatal = runtimePreflightHasFatal($fvplusRuntimePreflig
+
@@ -94,6 +96,7 @@ $fvplusRuntimePreflightHasFatal = runtimePreflightHasFatal($fvplusRuntimePreflig
+
html_entity_decode(
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.VMs.page b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.VMs.page
index 99bf2140..7c13fd96 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.VMs.page
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/folderview.plus.VMs.page
@@ -48,6 +48,7 @@ $fvplusRuntimePreflightHasFatal = runtimePreflightHasFatal($fvplusRuntimePreflig
+
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ar.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ar.json
index af3e07eb..f5fe7b09 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ar.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ar.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "rtl",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ar",
"native-name": "العربية",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "إضافة مجلد",
"up-to-date": "محدثة",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/bn.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/bn.json
index 9253bf4a..c30af4f3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/bn.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/bn.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "bn",
"native-name": "বাংলা",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "ফোল্ডার যোগ করুন",
"up-to-date": "আপ টু ডেট",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ca.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ca.json
index a97fb6b5..7bf9aa42 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ca.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ca.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ca",
"native-name": "Català",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Afegeix una carpeta",
"up-to-date": "al dia",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/cs.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/cs.json
index 48438833..a156615f 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/cs.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/cs.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "cs",
"native-name": "Čeština",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Přidat složku",
"up-to-date": "aktuální",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/da.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/da.json
index 77cea71d..90e1bd20 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/da.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/da.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "da",
"native-name": "Dansk",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Tilføj mappe",
"up-to-date": "up-to-date",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/de.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/de.json
index 3bda791d..d2891e43 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/de.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/de.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "de",
"native-name": "Deutsch",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Ordner hinzufügen",
"up-to-date": "aktuell",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/en.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/en.json
index bf265f6f..8b832ef8 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/en.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/en.json
@@ -3,15 +3,15 @@
"authors": [],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "en",
"native-name": "English",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "source",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Add Folder",
"up-to-date": "up-to-date",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/es.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/es.json
index fe4ef72c..6bbdf57b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/es.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/es.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "es",
"native-name": "Español",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Agregar carpeta",
"up-to-date": "actualizado",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/extraction-report.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/extraction-report.json
index 3f709d85..5515620b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/extraction-report.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/extraction-report.json
@@ -1,17 +1,17 @@
{
"catalog-version": "2026.08.10.1",
- "generated-at": "2026-08-18",
+ "generated-at": "2026-08-28",
"candidate-count": 0,
- "auto-bound-message-count": 1574,
- "catalog-message-count": 2122,
+ "auto-bound-message-count": 1579,
+ "catalog-message-count": 2168,
"description": "All detected initial and runtime-written legacy UI text is represented by the generated legacy-surface namespace.",
"largest-surfaces": {
"FolderViewPlus.page": 552,
"scripts/folderviewplus.js": 240,
- "Folder.page": 211,
+ "Folder.page": 212,
"scripts/folderviewplus.wizard.js": 196,
- "scripts/folder.js": 121,
- "scripts/folder.editor.chrome.js": 94,
+ "scripts/folder.js": 122,
+ "scripts/folder.editor.chrome.js": 99,
"scripts/folderviewplus.import.js": 76,
"scripts/folderviewplus.settings-workspaces.js": 56,
"scripts/docker.js": 54,
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/fr.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/fr.json
index c2365610..2ba7a009 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/fr.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/fr.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "fr",
"native-name": "Français",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Ajouter un dossier",
"up-to-date": "à jour",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hr.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hr.json
index a545f92f..cd0f2caf 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hr.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hr.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "hr",
"native-name": "Hrvatski",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Dodaj mapu",
"up-to-date": "ažuran",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hu.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hu.json
index bb75677a..41fbf909 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hu.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/hu.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "hu",
"native-name": "Magyar",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Mappa hozzáadása",
"up-to-date": "naprakész",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/it.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/it.json
index 9d4e48da..1d549b9d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/it.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/it.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "it",
"native-name": "Italiano",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Aggiungi cartella",
"up-to-date": "aggiornato",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ja.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ja.json
index f9b6e32c..759262b3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ja.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ja.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ja",
"native-name": "日本語",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "フォルダーを追加",
"up-to-date": "最新の状態",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ko.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ko.json
index 8b08c3d7..7cebc0b1 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ko.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ko.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ko",
"native-name": "한국어",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "폴더 추가",
"up-to-date": "최신 상태",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/lv.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/lv.json
index 8242e6d1..6ff159a3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/lv.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/lv.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "lv",
"native-name": "Latviešu",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Pievienot mapi",
"up-to-date": "aktuāls",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/docker.json
index 84a1cecb..9abd2b81 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "إخفاء عناوين IP العامة",
"docker.privacy.mask-network-interfaces": "قناع واجهات الشبكة",
"docker.privacy.mask-external-urls": "إخفاء عناوين URL الخارجية",
- "docker.actions.capture-diagnostics": "التقاط تشخيصات الصفحة"
+ "docker.actions.capture-diagnostics": "التقاط تشخيصات الصفحة",
+ "docker.folder.hide": "إخفاء المجلد",
+ "docker.folder.restore": "استعادة المجلد",
+ "docker.folder.restore-hidden-branch": "استعادة الفرع المخفي",
+ "docker.folder.hidden-indicator": "مخفي",
+ "docker.folder.unnamed": "مجلد",
+ "docker.folder.visibility-failed": "لا يمكن حفظ تفضيلات رؤية المجلد.",
+ "docker.visibility.hidden-folders": "المجلدات المخفية",
+ "docker.visibility.restore-all": "استعادة كافة المجلدات المخفية",
+ "docker.visibility.hidden-title": "المجلد مخفي",
+ "docker.visibility.folder-fallback": "مجلد",
+ "docker.visibility.hidden-message": "تم إخفاؤه من عرض Docker.",
+ "docker.visibility.undo": "تراجع",
+ "docker.visibility.all-hidden-title": "كافة صفوف المجلدات مخفية.",
+ "docker.visibility.all-hidden-help": "اكشف عنها مؤقتًا أو قم باستعادة كل مجلد مخفي.",
+ "docker.visibility.show-hidden": "إظهار المجلدات المخفية",
+ "docker.webui-profiles.open": "افتح ملف تعريف WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 من $3 جاهز)",
+ "docker.webui-profiles.manage": "إدارة ملفات تعريف WebUI",
+ "docker.webui-profiles.none-title": "لا توجد واجهات WebUI جاهزة",
+ "docker.webui-profiles.none-body": "لا يتم تشغيل أي من الحاويات المحددة باستخدام واجهة WebUI المتوفرة."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/editor.json
index 262ee198..d694e3ff 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "يؤلف المشروع يساوي",
"editor.rules.templates.compose-project-placeholder": "اسم المشروع (مثال: الوسائط)",
"editor.rules.templates.label-equals": "تسمية عامل الميناء يساوي",
- "editor.rules.templates.label-equals-placeholder": "مفتاح التسمية = القيمة"
+ "editor.rules.templates.label-equals-placeholder": "مفتاح التسمية = القيمة",
+ "editor.webui-profiles.title": "ملفات تعريف WebUI المخصصة",
+ "editor.webui-profiles.help": "أنشئ مجموعات تشغيل قابلة لإعادة الاستخدام من أعضاء Docker المباشرين لهذا المجلد. يتم التحقق من عناوين WebUI الحالية وحالة التشغيل فقط عند فتح ملف تعريف.",
+ "editor.webui-profiles.add": "إضافة الملف الشخصي",
+ "editor.webui-profiles.count": "$1 الملف الشخصي (الملفات الشخصية) المحفوظة",
+ "editor.webui-profiles.empty": "لا توجد ملفات تعريف WebUI مخصصة حتى الآن.",
+ "editor.webui-profiles.name": "اسم الملف الشخصي",
+ "editor.webui-profiles.duplicate": "مكررة",
+ "editor.webui-profiles.move-up": "تحرك للأعلى",
+ "editor.webui-profiles.move-down": "تحرك للأسفل",
+ "editor.webui-profiles.delete": "حذف",
+ "editor.webui-profiles.search": "البحث عن أعضاء المجلد",
+ "editor.webui-profiles.select-webui": "حدد الكل باستخدام WebUI",
+ "editor.webui-profiles.clear": "مسح التحديد",
+ "editor.webui-profiles.webui-ready": "تم تكوين WebUI",
+ "editor.webui-profiles.webui-runtime": "تم فحص WebUI عند الإطلاق",
+ "editor.webui-profiles.unavailable": "غير متوفر في هذا المجلد",
+ "editor.webui-profiles.no-members": "لا يتوفر أي أعضاء مجلد متطابقين.",
+ "editor.webui-profiles.error-name": "يحتاج كل ملف تعريف WebUI إلى اسم.",
+ "editor.webui-profiles.error-duplicate": "يجب أن تكون أسماء ملفات تعريف WebUI فريدة داخل هذا المجلد.",
+ "editor.webui-profiles.error-members": "يجب أن يحدد كل ملف تعريف WebUI عضو مجلد واحد على الأقل.",
+ "editor.webui-profiles.limit": "لقد وصل هذا المجلد إلى حد أمان ملف تعريف WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/legacy-surface.json
index a423e357..85b174e5 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ar/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "لا توجد تغييرات في التطبيق",
"legacy.surface.26f0360c9dddc4d1": "تم حذف الإعداد المسبق",
"legacy.surface.270bca2990500a60": "اختر مكان وجود هذا المجلد في التسلسل الهرمي.",
+ "legacy.surface.273c468809bda389": "قم بتعيين ألوان المؤشر ولون نص الحالة المستقل الذي تستخدمه صفوف المجلد.",
"legacy.surface.273f838c039ee10f": "اكتشف FolderView Plus أن صفحة الإعدادات أصبحت فارغة قبل اكتمال التهيئة.",
"legacy.surface.2753807153755a5e": "دمج (إضافة + تحديث)",
"legacy.surface.2847d4f584d08e0c": "جارٍ تحميل عرض أمر Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "بدء الفرع ($1)",
"legacy.surface.32c919087b37ec2d": "مكدس الوسائط",
"legacy.surface.334277e71d658735": "بعد الهدف",
+ "legacy.surface.33541731c2a77842": "قم بتعيين ألوان المؤشرات \"البدء\" و\"الإيقاف المؤقت\" و\"التوقف\" بشكل مستقل عن لون نص الحالة عبر طرق عرض Docker وVMs وDashboard.",
"legacy.surface.3388c6d7238cdde6": "عنوان إعدادات البرنامج المساعد",
"legacy.surface.33891d013966c572": "حدد مجلدًا للمهمة المجمعة.",
"legacy.surface.33c5850b76f401e8": "مجلد المستوى الأعلى",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "اختر تغيير الحالة والأعضاء الذين يتحكمون بهم.",
"legacy.surface.613aa3b591bacd9a": "إجمالي مساحة التخزين (الأقراص المدعومة بالملفات): $1",
"legacy.surface.61c56bacd53c2dfa": "ألوان شيفرون:",
+ "legacy.surface.61ea108a3718792d": "قم بإنشاء مجموعات مسماة تفتح فقط واجهات WebUI المحددة لحاوية Docker.",
"legacy.surface.61f565ba05bd647d": "سيتم تضمين أسماء الحاويات أو الأجهزة الافتراضية المطابقة هنا تلقائيًا.",
"legacy.surface.61fcdf8c82cb687a": "هل تريد تطبيق المهام التي تمت معاينتها؟",
"legacy.surface.6233afa7701a7fb8": "تضمين المعروض",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "تذبذب",
"legacy.surface.7179ea0035fca783": "مسح المرشحات",
"legacy.surface.7188848298a0b500": "النطاق: المفضلة",
+ "legacy.surface.71988c4d8e0803ba": "نص",
"legacy.surface.719e8555bf1abeba": "استخدم خطوة النماذج أو قم باستيراد الملفات أو إنشاء المجلدات يدويًا أولاً.",
"legacy.surface.71b2e8d085cdca1e": "جاهز: تم اجتياز جميع الشيكات.",
"legacy.surface.7205aff54d71cb8b": "انسخ إعدادات المجلد من مجلد Docker آخر أولاً.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "معاينة:",
"legacy.surface.75bf7d9afc988cb6": "يتم فرز المجلدات حسب الاسم من الألف إلى الياء. وتظل المجلدات المتداخلة داخل فرعها الأصلي.",
"legacy.surface.75d599007fcf49af": "كل العضوية",
- "legacy.surface.75d7a33335c4f033": "قم بتعيين الألوان التي تم البدء بها، والمتوقفة مؤقتًا، والمتوقفة التي تستخدمها صفوف المجلدات.",
"legacy.surface.7611fe57a952da4b": "موصى به. يحظر التغييرات المحفوفة بالمخاطر تلقائيًا.",
"legacy.surface.764210a29b968296": "فئة القالب",
"legacy.surface.764b74dea7f342ff": "يتحكم في عدد مستويات المجلدات المتداخلة التي تظهر كشرائح عند عرض المجلدات الفرعية في المعاينة المطوية.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "هيكل جدول VM",
"legacy.surface.8569ce7917f23eb6": "هل تريد حذف $1 القاعدة (القواعد) المحددة؟",
"legacy.surface.858e4ba7a29fd38b": "وقفة",
+ "legacy.surface.85b6c17862a72780": "ملفات تعريف WebUI",
"legacy.surface.85c83f4500bb9f2c": "معلومات وقت تشغيل VM",
"legacy.surface.85e2efe37bad1a44": "لا توجد عناصر مؤهلة لهذا الإجراء.",
"legacy.surface.8600edb883562f2c": "قناع عناوين IP للشبكة المحلية",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "قم بفحص إعداد مجلد Docker وVM الحالي لديك.",
"legacy.surface.916b423aa46d7493": "تم اكتشاف $1 مشكلة (مشكلات) استشارية (عمق/فرع فارغ)، ولكن لا توجد أخطاء في الارتباط اليتيم/الدائري للإصلاح التلقائي.",
"legacy.surface.91f41a1797b442ab": "تم تحديث $1 من القالب المحفوظ.",
- "legacy.surface.920b3e3ce5710d1d": "قم بتعيين ألوان مخصصة لأيقونة حالة هذا المجلد والنص عبر طرق عرض Docker وVMs وDashboard.",
"legacy.surface.920e413c7d411b61": "الحالة",
"legacy.surface.9213c7f4046c085d": "تحذير عتبة الاهتمام",
"legacy.surface.921b15089caf4ff8": "3. مراجعة ترتيب القواعد",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "يتحكم في كيفية تأثير التحديثات المعلقة على صحة هذا المجلد.",
"legacy.surface.926e4eab47bf42ea": "المباريات المباشرة: أدخل نمط regex.",
"legacy.surface.92c1cdfdf4cb9cf6": "ممكّن",
+ "legacy.surface.92cff14b4b7992dd": "حدد أعضاء المجلد المباشر الذي فتحه كل ملف تعريف مسمى.",
"legacy.surface.92ec9d61393d7064": "$1 التعبير العادي",
"legacy.surface.93aa7d6a1b93167d": "لم يتم العثور على أيقونات مخصصة. قم بتحميل رمز للبدء.",
"legacy.surface.93b1554a829a9433": "3 مستويات",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "التغييرات غير المحفوظة",
"legacy.surface.a71d508e50432f40": "هوملاب",
"legacy.surface.a7248eeb45eb1ce2": "عادي",
- "legacy.surface.a759d24fa1d32402": "افتح كافة واجهات WebUI",
"legacy.surface.a75ee6a28c39f865": "اسحب المجلدات الفرعية للتحكم في الترتيب الموضح في المعاينات والصفوف الموسعة.",
"legacy.surface.a76ac50faa110d7d": "أقدم المجلدات التي تم إنشاؤها أولاً",
"legacy.surface.a76e13b9839270eb": "استعادة",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "التشغيل الجاف",
"legacy.surface.d640c7421da06661": "استئناف",
"legacy.surface.d66d27cf2aa666c8": "عرض الاختيارات",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "الملف الشخصي",
"legacy.surface.d69b03bf0620f859": "إحضار إعداد FolderView موجود.",
"legacy.surface.d6a226a96bdc60fe": "منع النوافذ المنبثقة",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "قارن",
"legacy.surface.e89b30aa1dc30a6a": "الموارد",
"legacy.surface.e89c3109e1f14c41": "المجلد الهدف",
+ "legacy.surface.e8b4d99f2d672a8e": "ملفات تعريف WebUI المخصصة",
"legacy.surface.e8e470943fd0f0b6": "ترتيب المجلدات المتداخلة",
"legacy.surface.e948f182d485afee": "قم بتوسيع الفرع",
"legacy.surface.e94d3ee06ecf6aac": "فشل التصدير",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/docker.json
index 9793ffbe..761c14e0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "পাবলিক আইপি মাস্ক করুন",
"docker.privacy.mask-network-interfaces": "মাস্ক নেটওয়ার্ক ইন্টারফেস",
"docker.privacy.mask-external-urls": "এক্সটার্নাল ইউআরএল মাস্ক করুন",
- "docker.actions.capture-diagnostics": "পৃষ্ঠা ডায়াগনস্টিক ক্যাপচার করুন"
+ "docker.actions.capture-diagnostics": "পৃষ্ঠা ডায়াগনস্টিক ক্যাপচার করুন",
+ "docker.folder.hide": "ফোল্ডার লুকান",
+ "docker.folder.restore": "ফোল্ডার পুনরুদ্ধার করুন",
+ "docker.folder.restore-hidden-branch": "লুকানো শাখা পুনরুদ্ধার করুন",
+ "docker.folder.hidden-indicator": "লুকানো",
+ "docker.folder.unnamed": "ফোল্ডার",
+ "docker.folder.visibility-failed": "ফোল্ডার দৃশ্যমানতা পছন্দ সংরক্ষণ করা যায়নি.",
+ "docker.visibility.hidden-folders": "লুকানো ফোল্ডার",
+ "docker.visibility.restore-all": "সমস্ত লুকানো ফোল্ডার পুনরুদ্ধার করুন",
+ "docker.visibility.hidden-title": "ফোল্ডার লুকানো",
+ "docker.visibility.folder-fallback": "ফোল্ডার",
+ "docker.visibility.hidden-message": "ডকার ভিউ থেকে লুকানো ছিল।",
+ "docker.visibility.undo": "পূর্বাবস্থায় ফেরান",
+ "docker.visibility.all-hidden-title": "সমস্ত ফোল্ডার সারি লুকানো হয়.",
+ "docker.visibility.all-hidden-help": "তাদের সাময়িকভাবে প্রকাশ করুন বা প্রতিটি লুকানো ফোল্ডার পুনরুদ্ধার করুন।",
+ "docker.visibility.show-hidden": "লুকানো ফোল্ডার দেখান",
+ "docker.webui-profiles.open": "WebUI প্রোফাইল খুলুন",
+ "docker.webui-profiles.ready": "$1 ($3 এর মধ্যে $2 প্রস্তুত)",
+ "docker.webui-profiles.manage": "WebUI প্রোফাইল পরিচালনা করুন",
+ "docker.webui-profiles.none-title": "কোনো WebUI প্রস্তুত নয়",
+ "docker.webui-profiles.none-body": "নির্বাচিত কন্টেইনারগুলির একটিও উপলব্ধ WebUI এর সাথে চলছে না৷"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/editor.json
index c2bea590..b4283137 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "রচনা প্রকল্প সমান",
"editor.rules.templates.compose-project-placeholder": "প্রকল্পের নাম (উদাহরণ: মিডিয়া)",
"editor.rules.templates.label-equals": "ডকার লেবেল সমান",
- "editor.rules.templates.label-equals-placeholder": "লেবেল কী = মান"
+ "editor.rules.templates.label-equals-placeholder": "লেবেল কী = মান",
+ "editor.webui-profiles.title": "কাস্টম WebUI প্রোফাইল",
+ "editor.webui-profiles.help": "এই ফোল্ডারের সরাসরি ডকার সদস্যদের থেকে পুনরায় ব্যবহারযোগ্য লঞ্চ গ্রুপ তৈরি করুন। একটি প্রোফাইল খোলা হলেই বর্তমান WebUI ঠিকানা এবং চলমান অবস্থা চেক করা হয়।",
+ "editor.webui-profiles.add": "প্রোফাইল যোগ করুন",
+ "editor.webui-profiles.count": "$1 সংরক্ষিত প্রোফাইল(গুলি)",
+ "editor.webui-profiles.empty": "এখনও কোনো কাস্টম WebUI প্রোফাইল নেই৷",
+ "editor.webui-profiles.name": "প্রোফাইল নাম",
+ "editor.webui-profiles.duplicate": "ডুপ্লিকেট",
+ "editor.webui-profiles.move-up": "উপরে সরান",
+ "editor.webui-profiles.move-down": "নিচে সরান",
+ "editor.webui-profiles.delete": "মুছুন",
+ "editor.webui-profiles.search": "ফোল্ডার সদস্যদের অনুসন্ধান করুন",
+ "editor.webui-profiles.select-webui": "WebUI দিয়ে সব নির্বাচন করুন",
+ "editor.webui-profiles.clear": "নির্বাচন পরিষ্কার করুন",
+ "editor.webui-profiles.webui-ready": "WebUI কনফিগার করা হয়েছে",
+ "editor.webui-profiles.webui-runtime": "WebUI লঞ্চের সময় পরীক্ষা করা হয়েছে",
+ "editor.webui-profiles.unavailable": "এই ফোল্ডারে অনুপলব্ধ",
+ "editor.webui-profiles.no-members": "কোন মিল ফোল্ডার সদস্য উপলব্ধ নেই.",
+ "editor.webui-profiles.error-name": "প্রতিটি WebUI প্রোফাইলের একটি নাম প্রয়োজন৷",
+ "editor.webui-profiles.error-duplicate": "WebUI প্রোফাইল নাম এই ফোল্ডারের মধ্যে অনন্য হতে হবে।",
+ "editor.webui-profiles.error-members": "প্রতিটি WebUI প্রোফাইলকে অবশ্যই অন্তত একজন ফোল্ডার সদস্য নির্বাচন করতে হবে।",
+ "editor.webui-profiles.limit": "এই ফোল্ডারটি WebUI প্রোফাইল নিরাপত্তা সীমাতে পৌঁছেছে।"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/legacy-surface.json
index cf1e97b7..ab3461e3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/bn/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "কোনো অ্যাপ পরিবর্তন হয় না",
"legacy.surface.26f0360c9dddc4d1": "প্রিসেট মুছে ফেলা হয়েছে",
"legacy.surface.270bca2990500a60": "ক্রমানুসারে এই ফোল্ডারটি কোথায় থাকে তা চয়ন করুন৷",
+ "legacy.surface.273c468809bda389": "ফোল্ডার সারি দ্বারা ব্যবহৃত সূচক রং এবং স্বাধীন স্থিতি পাঠ্য রঙ সেট করুন।",
"legacy.surface.273f838c039ee10f": "FolderView Plus শনাক্ত করেছে যে শুরু করার আগে সেটিংস পৃষ্ঠাটি ফাঁকা হয়ে গেছে।",
"legacy.surface.2753807153755a5e": "মার্জ (সংযোজন + আপডেট)",
"legacy.surface.2847d4f584d08e0c": "ডকার কমান্ড ভিউ লোড হচ্ছে...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "শাখা শুরু করুন ($1)",
"legacy.surface.32c919087b37ec2d": "মিডিয়া স্ট্যাক",
"legacy.surface.334277e71d658735": "লক্ষ্যের পর",
+ "legacy.surface.33541731c2a77842": "ডকার, ভিএম এবং ড্যাশবোর্ড ভিউ জুড়ে স্ট্যাটাস টেক্সট রঙ থেকে স্বাধীনভাবে স্টার্টড, পজড এবং স্টপড ইন্ডিকেটর রঙ সেট করুন।",
"legacy.surface.3388c6d7238cdde6": "প্লাগইন সেটিংস শিরোনাম",
"legacy.surface.33891d013966c572": "বাল্ক অ্যাসাইনমেন্টের জন্য একটি ফোল্ডার নির্বাচন করুন।",
"legacy.surface.33c5850b76f401e8": "শীর্ষ-স্তরের ফোল্ডার",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "রাজ্যের পরিবর্তন এবং এটি কোন সদস্যদের নিয়ন্ত্রণ করে তা বেছে নিন।",
"legacy.surface.613aa3b591bacd9a": "মোট স্টোরেজ (ফাইল-ব্যাকড ডিস্ক): $1",
"legacy.surface.61c56bacd53c2dfa": "শেভরন রং:",
+ "legacy.surface.61ea108a3718792d": "নামযুক্ত গোষ্ঠীগুলি তৈরি করুন যা শুধুমাত্র নির্বাচিত ডকার কন্টেইনার WebUI গুলি খুলবে৷",
"legacy.surface.61f565ba05bd647d": "ম্যাচিং ধারক বা VM নামগুলি এখানে স্বয়ংক্রিয়ভাবে অন্তর্ভুক্ত হবে৷",
"legacy.surface.61fcdf8c82cb687a": "প্রিভিউ করা অ্যাসাইনমেন্ট প্রয়োগ করবেন?",
"legacy.surface.6233afa7701a7fb8": "দেখানো অন্তর্ভুক্ত",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "পরচুলা",
"legacy.surface.7179ea0035fca783": "ফিল্টার সাফ করুন",
"legacy.surface.7188848298a0b500": "সুযোগ: প্রিয়",
+ "legacy.surface.71988c4d8e0803ba": "পাঠ্য",
"legacy.surface.719e8555bf1abeba": "প্রথমে টেমপ্লেট ধাপ ব্যবহার করুন, ফাইল আমদানি করুন বা ম্যানুয়ালি ফোল্ডার তৈরি করুন।",
"legacy.surface.71b2e8d085cdca1e": "প্রস্তুত: সমস্ত চেক পাস।",
"legacy.surface.7205aff54d71cb8b": "প্রথমে অন্য ডকার ফোল্ডার থেকে ফোল্ডার সেটিংস অনুলিপি করুন।",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "পূর্বরূপ:",
"legacy.surface.75bf7d9afc988cb6": "ফোল্ডারগুলি A থেকে Z পর্যন্ত নাম অনুসারে বাছাই করা হয়৷ নেস্টেড ফোল্ডারগুলি এখনও তাদের মূল শাখার ভিতরে থাকে৷",
"legacy.surface.75d599007fcf49af": "সমস্ত সদস্যপদ",
- "legacy.surface.75d7a33335c4f033": "ফোল্ডার সারি দ্বারা ব্যবহৃত শুরু, বিরতি, এবং বন্ধ রং সেট করুন।",
"legacy.surface.7611fe57a952da4b": "প্রস্তাবিত. ঝুঁকিপূর্ণ পরিবর্তনগুলি স্বয়ংক্রিয়ভাবে ব্লক করে।",
"legacy.surface.764210a29b968296": "টেমপ্লেট বিভাগ",
"legacy.surface.764b74dea7f342ff": "নিয়ন্ত্রন করে যে কতগুলি নেস্টেড ফোল্ডার স্তর চিপ হিসাবে প্রদর্শিত হবে যখন চাইল্ড ফোল্ডারগুলিকে সঙ্কুচিত পূর্বরূপ দেখানো হয়৷",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM টেবিল বডি",
"legacy.surface.8569ce7917f23eb6": "$1 নির্বাচিত নিয়ম(গুলি) মুছবেন?",
"legacy.surface.858e4ba7a29fd38b": "বিরতি",
+ "legacy.surface.85b6c17862a72780": "WebUI প্রোফাইল",
"legacy.surface.85c83f4500bb9f2c": "VM রানটাইম তথ্য",
"legacy.surface.85e2efe37bad1a44": "এই কর্মের জন্য কোন যোগ্য আইটেম.",
"legacy.surface.8600edb883562f2c": "ল্যান আইপি মাস্ক করুন",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "আপনার বর্তমান ডকার এবং ভিএম ফোল্ডার সেটআপ স্ক্যান করুন।",
"legacy.surface.916b423aa46d7493": "সনাক্ত করা হয়েছে $1 পরামর্শক সমস্যা(গুলি) (গভীরতা/খালি শাখা), কিন্তু স্বয়ংক্রিয় মেরামতের জন্য কোনো অনাথ/সাইকেল লিঙ্ক ত্রুটি নেই৷",
"legacy.surface.91f41a1797b442ab": "সংরক্ষিত টেমপ্লেট থেকে $1 আপডেট করা হয়েছে।",
- "legacy.surface.920b3e3ce5710d1d": "এই ফোল্ডারের স্ট্যাটাস আইকন এবং ডকার, ভিএম এবং ড্যাশবোর্ড ভিউ জুড়ে পাঠ্যের জন্য কাস্টম রং সেট করুন।",
"legacy.surface.920e413c7d411b61": "স্ট্যাটাস",
"legacy.surface.9213c7f4046c085d": "মনোযোগের জন্য থ্রেশহোল্ড সতর্ক করুন",
"legacy.surface.921b15089caf4ff8": "3. নিয়ম আদেশ পর্যালোচনা করুন",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "মুলতুবি আপডেটগুলি কীভাবে এই ফোল্ডারের স্বাস্থ্যকে প্রভাবিত করে তা নিয়ন্ত্রণ করে।",
"legacy.surface.926e4eab47bf42ea": "লাইভ ম্যাচ: একটি regex প্যাটার্ন লিখুন।",
"legacy.surface.92c1cdfdf4cb9cf6": "সক্রিয়",
+ "legacy.surface.92cff14b4b7992dd": "প্রতিটি নামযুক্ত প্রোফাইল দ্বারা খোলা সরাসরি ফোল্ডার সদস্য নির্বাচন করুন।",
"legacy.surface.92ec9d61393d7064": "$1 রেজেক্স",
"legacy.surface.93aa7d6a1b93167d": "কোন কাস্টম আইকন পাওয়া যায়নি. শুরু করতে একটি আইকন আপলোড করুন।",
"legacy.surface.93b1554a829a9433": "3 স্তর",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "অসংরক্ষিত পরিবর্তন",
"legacy.surface.a71d508e50432f40": "হোমল্যাব",
"legacy.surface.a7248eeb45eb1ce2": "স্বাভাবিক",
- "legacy.surface.a759d24fa1d32402": "সমস্ত WebUI খুলুন",
"legacy.surface.a75ee6a28c39f865": "পূর্বরূপ এবং প্রসারিত সারিতে দেখানো ক্রম নিয়ন্ত্রণ করতে চাইল্ড ফোল্ডার টেনে আনুন।",
"legacy.surface.a76ac50faa110d7d": "প্রাচীনতম তৈরি ফোল্ডার প্রথমে",
"legacy.surface.a76e13b9839270eb": "পুনরুদ্ধার করুন",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "ড্রাই রান",
"legacy.surface.d640c7421da06661": "পুনরায় শুরু করুন",
"legacy.surface.d66d27cf2aa666c8": "প্রদর্শন পছন্দ",
+ "legacy.surface.d693881b5ac21882": "--fvplus-ফোল্ডার-স্থিতি-টেক্সট",
"legacy.surface.d696a35bdd1883da": "প্রোফাইল",
"legacy.surface.d69b03bf0620f859": "একটি বিদ্যমান FolderView সেটআপ আনুন।",
"legacy.surface.d6a226a96bdc60fe": "পপ আপ ব্লক করুন",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "তুলনা করুন",
"legacy.surface.e89b30aa1dc30a6a": "সম্পদ",
"legacy.surface.e89c3109e1f14c41": "লক্ষ্য ফোল্ডার",
+ "legacy.surface.e8b4d99f2d672a8e": "কাস্টম WebUI প্রোফাইল",
"legacy.surface.e8e470943fd0f0b6": "নেস্টেড ফোল্ডার অর্ডার",
"legacy.surface.e948f182d485afee": "শাখা প্রসারিত করুন",
"legacy.surface.e94d3ee06ecf6aac": "রপ্তানি ব্যর্থ হয়েছে",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/docker.json
index 9d882b53..0bbf36b8 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Enmascarar les IP públiques",
"docker.privacy.mask-network-interfaces": "Enmascarar interfícies de xarxa",
"docker.privacy.mask-external-urls": "Enmascarar URL externs",
- "docker.actions.capture-diagnostics": "Captura el diagnòstic de la pàgina"
+ "docker.actions.capture-diagnostics": "Captura el diagnòstic de la pàgina",
+ "docker.folder.hide": "Amaga la carpeta",
+ "docker.folder.restore": "Restaura la carpeta",
+ "docker.folder.restore-hidden-branch": "Restaura la branca oculta",
+ "docker.folder.hidden-indicator": "Ocult",
+ "docker.folder.unnamed": "Carpeta",
+ "docker.folder.visibility-failed": "La preferència de visibilitat de la carpeta no s'ha pogut desar.",
+ "docker.visibility.hidden-folders": "Carpetes ocultes",
+ "docker.visibility.restore-all": "Restaura totes les carpetes ocultes",
+ "docker.visibility.hidden-title": "Carpeta oculta",
+ "docker.visibility.folder-fallback": "Carpeta",
+ "docker.visibility.hidden-message": "s'ha amagat de la vista Docker.",
+ "docker.visibility.undo": "Desfer",
+ "docker.visibility.all-hidden-title": "Totes les files de carpetes estan amagades.",
+ "docker.visibility.all-hidden-help": "Revela'ls temporalment o restaura totes les carpetes ocultes.",
+ "docker.visibility.show-hidden": "Mostra les carpetes ocultes",
+ "docker.webui-profiles.open": "Obriu el perfil d'UI Web",
+ "docker.webui-profiles.ready": "$1 ($2 de $3 llest)",
+ "docker.webui-profiles.manage": "Gestionar perfils WebUI",
+ "docker.webui-profiles.none-title": "No hi ha cap IU preparada",
+ "docker.webui-profiles.none-body": "Cap dels contenidors seleccionats s'està executant amb una UI disponible."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/editor.json
index e4780cef..50430109 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Redacta el projecte igual",
"editor.rules.templates.compose-project-placeholder": "Nom del projecte (exemple: mitjans de comunicació)",
"editor.rules.templates.label-equals": "L'etiqueta Docker és igual",
- "editor.rules.templates.label-equals-placeholder": "Etiqueta clau=valor"
+ "editor.rules.templates.label-equals-placeholder": "Etiqueta clau=valor",
+ "editor.webui-profiles.title": "Perfils de WebUI personalitzats",
+ "editor.webui-profiles.help": "Creeu grups de llançament reutilitzables a partir dels membres directes de Docker d'aquesta carpeta. Les adreces de la WebUI actuals i l'estat d'execució només es comproven quan s'obre un perfil.",
+ "editor.webui-profiles.add": "Afegeix un perfil",
+ "editor.webui-profiles.count": "$1 perfils desats",
+ "editor.webui-profiles.empty": "Encara no hi ha perfils de WebUI personalitzats.",
+ "editor.webui-profiles.name": "Nom del perfil",
+ "editor.webui-profiles.duplicate": "Duplicat",
+ "editor.webui-profiles.move-up": "Mou cap amunt",
+ "editor.webui-profiles.move-down": "Mou cap avall",
+ "editor.webui-profiles.delete": "Suprimeix",
+ "editor.webui-profiles.search": "Cerca membres de la carpeta",
+ "editor.webui-profiles.select-webui": "Seleccioneu-ho tot amb WebUI",
+ "editor.webui-profiles.clear": "Esborra la selecció",
+ "editor.webui-profiles.webui-ready": "WebUI configurat",
+ "editor.webui-profiles.webui-runtime": "WebUI comprovat al llançament",
+ "editor.webui-profiles.unavailable": "No disponible en aquesta carpeta",
+ "editor.webui-profiles.no-members": "No hi ha membres de carpeta coincidents disponibles.",
+ "editor.webui-profiles.error-name": "Cada perfil de WebUI necessita un nom.",
+ "editor.webui-profiles.error-duplicate": "Els noms de perfil de la UI web han de ser únics dins d'aquesta carpeta.",
+ "editor.webui-profiles.error-members": "Cada perfil de WebUI ha de seleccionar almenys un membre de la carpeta.",
+ "editor.webui-profiles.limit": "Aquesta carpeta ha assolit el límit de seguretat del perfil WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/legacy-surface.json
index 85917b42..537ce20e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ca/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "No hi ha canvis d'aplicació",
"legacy.surface.26f0360c9dddc4d1": "S'ha suprimit el valor predefinit",
"legacy.surface.270bca2990500a60": "Trieu on es troba aquesta carpeta a la jerarquia.",
+ "legacy.surface.273c468809bda389": "Estableix els colors dels indicadors i el color del text d'estat independent utilitzat per les files de carpetes.",
"legacy.surface.273f838c039ee10f": "FolderView Plus va detectar que la pàgina de configuració es va quedar en blanc abans de completar la inicialització.",
"legacy.surface.2753807153755a5e": "Combina (afegeix + actualitza)",
"legacy.surface.2847d4f584d08e0c": "S'està carregant la vista d'ordres de Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Inicia branca ($1)",
"legacy.surface.32c919087b37ec2d": "Pila de mitjans",
"legacy.surface.334277e71d658735": "Després de l'objectiu",
+ "legacy.surface.33541731c2a77842": "Estableix els colors dels indicadors Iniciat, En pausa i Aturat independentment del color del text d'estat a les vistes de Docker, VMs i Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Títol de la configuració del connector",
"legacy.surface.33891d013966c572": "Seleccioneu una carpeta per a l'assignació massiva.",
"legacy.surface.33c5850b76f401e8": "Carpeta de primer nivell",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Trieu el canvi d'estat i quins membres controla.",
"legacy.surface.613aa3b591bacd9a": "Emmagatzematge total (discs amb còpia de seguretat de fitxers): $1",
"legacy.surface.61c56bacd53c2dfa": "Colors Chevron:",
+ "legacy.surface.61ea108a3718792d": "Creeu grups amb nom que només obrin les interfícies web del contenidor Docker seleccionades.",
"legacy.surface.61f565ba05bd647d": "Aquí s'inclouran automàticament els noms de contenidors o VM coincidents.",
"legacy.surface.61fcdf8c82cb687a": "Voleu aplicar les tasques amb vista prèvia?",
"legacy.surface.6233afa7701a7fb8": "Inclou mostrat",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Moveu-vos",
"legacy.surface.7179ea0035fca783": "Esborra els filtres",
"legacy.surface.7188848298a0b500": "Àmbit: favorits",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.719e8555bf1abeba": "Utilitzeu primer el pas Plantilles, importeu fitxers o creeu carpetes manualment.",
"legacy.surface.71b2e8d085cdca1e": "A punt: tots els controls superats.",
"legacy.surface.7205aff54d71cb8b": "Copieu primer la configuració de la carpeta d'una altra carpeta Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Vista prèvia:",
"legacy.surface.75bf7d9afc988cb6": "Les carpetes s'ordenen pel nom de la A a la Z. Les carpetes niuades encara romanen dins de la seva branca principal.",
"legacy.surface.75d599007fcf49af": "Tots els membres",
- "legacy.surface.75d7a33335c4f033": "Estableix els colors iniciats, aturats i aturats utilitzats per les files de carpetes.",
"legacy.surface.7611fe57a952da4b": "Recomanat. Bloqueja automàticament els canvis arriscats.",
"legacy.surface.764210a29b968296": "Categoria de plantilla",
"legacy.surface.764b74dea7f342ff": "Controla quants nivells de carpetes imbricades apareixen com a xips quan les carpetes secundàries es mostren a la vista prèvia reduïda.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Cos de la taula VM",
"legacy.surface.8569ce7917f23eb6": "Vols suprimir $1 regles seleccionades?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "Perfils WebUI",
"legacy.surface.85c83f4500bb9f2c": "Informació del temps d'execució de la VM",
"legacy.surface.85e2efe37bad1a44": "No hi ha elements aptes per a aquesta acció.",
"legacy.surface.8600edb883562f2c": "Enmascarar les IP de la LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Escaneja la configuració actual de la carpeta Docker i VM.",
"legacy.surface.916b423aa46d7493": "S'han detectat problemes d'assessorament $1 (branca de profunditat/buida), però no hi ha errors d'enllaç orfe/cicle per a la reparació automàtica.",
"legacy.surface.91f41a1797b442ab": "$1 actualitzat de la plantilla desada.",
- "legacy.surface.920b3e3ce5710d1d": "Estableix colors personalitzats per a la icona d'estat i el text d'aquesta carpeta a les vistes de Docker, VMs i Dashboard.",
"legacy.surface.920e413c7d411b61": "Estat",
"legacy.surface.9213c7f4046c085d": "Llindar d'advertència per a l'atenció",
"legacy.surface.921b15089caf4ff8": "3. Reviseu l'ordre de la regla",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controla com les actualitzacions pendents afecten la salut d'aquesta carpeta.",
"legacy.surface.926e4eab47bf42ea": "Coincidències en directe: introduïu un patró d'expressió regular.",
"legacy.surface.92c1cdfdf4cb9cf6": "Habilitat",
+ "legacy.surface.92cff14b4b7992dd": "Seleccioneu els membres directes de la carpeta oberts per cada perfil anomenat.",
"legacy.surface.92ec9d61393d7064": "$1 expressió regular",
"legacy.surface.93aa7d6a1b93167d": "No s'han trobat icones personalitzades. Carregueu una icona per començar.",
"legacy.surface.93b1554a829a9433": "3 nivells",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Canvis no desats",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Obriu totes les IU web",
"legacy.surface.a75ee6a28c39f865": "Arrossegueu les carpetes secundàries per controlar l'ordre que es mostra a les visualitzacions prèvies i a les files ampliades.",
"legacy.surface.a76ac50faa110d7d": "Primer les carpetes creades més antigues",
"legacy.surface.a76e13b9839270eb": "Restaurar",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Cursa en sec",
"legacy.surface.d640c7421da06661": "Currículum",
"legacy.surface.d66d27cf2aa666c8": "Mostra opcions",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Perfil",
"legacy.surface.d69b03bf0620f859": "Introduïu una configuració de FolderView existent.",
"legacy.surface.d6a226a96bdc60fe": "Bloqueja les finestres emergents",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Compara",
"legacy.surface.e89b30aa1dc30a6a": "Recursos",
"legacy.surface.e89c3109e1f14c41": "Carpeta de destinació",
+ "legacy.surface.e8b4d99f2d672a8e": "Perfils d'UI Web personalitzats",
"legacy.surface.e8e470943fd0f0b6": "Ordre de carpetes niuades",
"legacy.surface.e948f182d485afee": "Ampliar la branca",
"legacy.surface.e94d3ee06ecf6aac": "Ha fallat l'exportació",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/docker.json
index 6f8a29b2..40f40658 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maska pro veřejnost IPs",
"docker.privacy.mask-network-interfaces": "Maska síťových rozhraní",
"docker.privacy.mask-external-urls": "Maskovat externí URL",
- "docker.actions.capture-diagnostics": "Diagnostika snímací stránky"
+ "docker.actions.capture-diagnostics": "Diagnostika snímací stránky",
+ "docker.folder.hide": "Skrýt složku",
+ "docker.folder.restore": "Obnovit složku",
+ "docker.folder.restore-hidden-branch": "Obnovte skrytou větev",
+ "docker.folder.hidden-indicator": "Skrytá",
+ "docker.folder.unnamed": "Složka",
+ "docker.folder.visibility-failed": "Předvolbu viditelnosti složky nebylo možné uložit.",
+ "docker.visibility.hidden-folders": "Skryté složky",
+ "docker.visibility.restore-all": "Obnovte všechny skryté složky",
+ "docker.visibility.hidden-title": "Složka je skryta",
+ "docker.visibility.folder-fallback": "Složka",
+ "docker.visibility.hidden-message": "byl skryt ze zobrazení Docker.",
+ "docker.visibility.undo": "Vrátit zpět",
+ "docker.visibility.all-hidden-title": "Všechny řádky složek jsou skryté.",
+ "docker.visibility.all-hidden-help": "Dočasně je odhalte nebo obnovte každou skrytou složku.",
+ "docker.visibility.show-hidden": "Zobrazit skryté složky",
+ "docker.webui-profiles.open": "Otevřete profil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 z $3 připraveno)",
+ "docker.webui-profiles.manage": "Správa profilů WebUI",
+ "docker.webui-profiles.none-title": "Nejsou připravena žádná rozhraní WebUI",
+ "docker.webui-profiles.none-body": "Žádný z vybraných kontejnerů není spuštěn s dostupným rozhraním WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/editor.json
index d20c927f..073f18b4 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Sestavit projekt rovná se",
"editor.rules.templates.compose-project-placeholder": "Název projektu (příklad: média)",
"editor.rules.templates.label-equals": "Docker štítek se rovná",
- "editor.rules.templates.label-equals-placeholder": "Klíč štítku=hodnota"
+ "editor.rules.templates.label-equals-placeholder": "Klíč štítku=hodnota",
+ "editor.webui-profiles.title": "Vlastní profily WebUI",
+ "editor.webui-profiles.help": "Vytvořte znovu použitelné skupiny spouštění z přímých členů Dockeru této složky. Aktuální adresy webového rozhraní a stav běhu jsou kontrolovány pouze při otevření profilu.",
+ "editor.webui-profiles.add": "Přidat profil",
+ "editor.webui-profiles.count": "$1 uložené profily",
+ "editor.webui-profiles.empty": "Zatím žádné vlastní profily WebUI.",
+ "editor.webui-profiles.name": "Název profilu",
+ "editor.webui-profiles.duplicate": "Duplicitní",
+ "editor.webui-profiles.move-up": "Posuňte se nahoru",
+ "editor.webui-profiles.move-down": "Posuňte se dolů",
+ "editor.webui-profiles.delete": "Smazat",
+ "editor.webui-profiles.search": "Hledat členy složky",
+ "editor.webui-profiles.select-webui": "Vyberte vše pomocí WebUI",
+ "editor.webui-profiles.clear": "Jasný výběr",
+ "editor.webui-profiles.webui-ready": "WebUI nakonfigurováno",
+ "editor.webui-profiles.webui-runtime": "WebUI zkontrolováno při spuštění",
+ "editor.webui-profiles.unavailable": "V této složce není k dispozici",
+ "editor.webui-profiles.no-members": "Nejsou k dispozici žádní odpovídající členové složky.",
+ "editor.webui-profiles.error-name": "Každý profil WebUI potřebuje jméno.",
+ "editor.webui-profiles.error-duplicate": "Názvy profilů webového rozhraní musí být v této složce jedinečné.",
+ "editor.webui-profiles.error-members": "Každý profil WebUI musí vybrat alespoň jednoho člena složky.",
+ "editor.webui-profiles.limit": "Tato složka dosáhla bezpečnostního limitu profilu WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/legacy-surface.json
index d2826857..76a3bbc1 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/cs/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Žádné změny aplikace",
"legacy.surface.26f0360c9dddc4d1": "Předvolba smazána",
"legacy.surface.270bca2990500a60": "Vyberte umístění této složky v hierarchii.",
+ "legacy.surface.273c468809bda389": "Nastavte barvy indikátoru a barvu nezávislého stavového textu, kterou používají řádky složek.",
"legacy.surface.273f838c039ee10f": "FolderView Plus zjistil, že stránka Nastavení byla před dokončením inicializace prázdná.",
"legacy.surface.2753807153755a5e": "Sloučit (přidat + aktualizovat)",
"legacy.surface.2847d4f584d08e0c": "Načítání zobrazení příkazu Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Zahájit větev ($1)",
"legacy.surface.32c919087b37ec2d": "Zásobník médií",
"legacy.surface.334277e71d658735": "Po cíli",
+ "legacy.surface.33541731c2a77842": "Nastavte barvy indikátoru Spuštěno, Pozastaveno a Zastaveno nezávisle na barvě stavového textu v zobrazeních Docker, VM a Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Název nastavení pluginu",
"legacy.surface.33891d013966c572": "Vyberte složku pro hromadné přiřazení.",
"legacy.surface.33c5850b76f401e8": "Složka nejvyšší úrovně",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Vyberte změnu stavu a členy, které řídí.",
"legacy.surface.613aa3b591bacd9a": "Celková kapacita úložiště (disky zálohované soubory): $1",
"legacy.surface.61c56bacd53c2dfa": "Barvy Chevron:",
+ "legacy.surface.61ea108a3718792d": "Vytvořte pojmenované skupiny, které otevřou pouze vybrané webové uživatelské rozhraní kontejneru Docker.",
"legacy.surface.61f565ba05bd647d": "Zde budou automaticky zahrnuty odpovídající názvy kontejnerů nebo virtuálních počítačů.",
"legacy.surface.61fcdf8c82cb687a": "Použít úkoly v náhledu?",
"legacy.surface.6233afa7701a7fb8": "Zahrnout zobrazeno",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Kroutit se",
"legacy.surface.7179ea0035fca783": "Vymazat filtry",
"legacy.surface.7188848298a0b500": "Rozsah: oblíbené",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.719e8555bf1abeba": "Nejprve použijte krok Šablony, importujte soubory nebo vytvořte složky ručně.",
"legacy.surface.71b2e8d085cdca1e": "Připraveno: všechny kontroly prošly.",
"legacy.surface.7205aff54d71cb8b": "Nejprve zkopírujte nastavení složky z jiné složky Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Náhled:",
"legacy.surface.75bf7d9afc988cb6": "Složky jsou seřazeny podle názvu od A do Z. Vnořené složky stále zůstávají ve své nadřazené větvi.",
"legacy.surface.75d599007fcf49af": "Veškeré členství",
- "legacy.surface.75d7a33335c4f033": "Nastavte barvy spuštění, pozastavení a zastavení používané řádky složek.",
"legacy.surface.7611fe57a952da4b": "Doporučeno. Automaticky blokuje rizikové změny.",
"legacy.surface.764210a29b968296": "Kategorie šablony",
"legacy.surface.764b74dea7f342ff": "Určuje, kolik úrovní vnořených složek se zobrazí jako čipy, když se podřízené složky zobrazí ve sbaleném náhledu.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Tělo stolu VM",
"legacy.surface.8569ce7917f23eb6": "Smazat $1 vybraná pravidla?",
"legacy.surface.858e4ba7a29fd38b": "Pauza",
+ "legacy.surface.85b6c17862a72780": "WebUI profily",
"legacy.surface.85c83f4500bb9f2c": "Informace o běhovém prostředí virtuálního počítače",
"legacy.surface.85e2efe37bad1a44": "Pro tuto akci nejsou žádné vhodné položky.",
"legacy.surface.8600edb883562f2c": "Maskovat IP adresy LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Prohledejte aktuální nastavení složky Docker a virtuálního počítače.",
"legacy.surface.916b423aa46d7493": "Zjištěny $1 poradenský problém (hloubka/prázdná větev), ale žádné chyby osiřelých/cyklických odkazů pro automatickou opravu.",
"legacy.surface.91f41a1797b442ab": "Aktualizováno $1 z uložené šablony.",
- "legacy.surface.920b3e3ce5710d1d": "Nastavte vlastní barvy pro stavovou ikonu a text této složky v zobrazeních Docker, VM a Dashboard.",
"legacy.surface.920e413c7d411b61": "Stav",
"legacy.surface.9213c7f4046c085d": "Varovat práh pozornosti",
"legacy.surface.921b15089caf4ff8": "3. Zkontrolujte pořadí pravidel",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Řídí, jak čekající aktualizace ovlivní stav této složky.",
"legacy.surface.926e4eab47bf42ea": "Živé shody: zadejte vzor regulárního výrazu.",
"legacy.surface.92c1cdfdf4cb9cf6": "Povoleno",
+ "legacy.surface.92cff14b4b7992dd": "Vyberte přímé členy složky otevřené každým pojmenovaným profilem.",
"legacy.surface.92ec9d61393d7064": "$1 regulární výraz",
"legacy.surface.93aa7d6a1b93167d": "Nebyly nalezeny žádné vlastní ikony. Začněte nahráním ikony.",
"legacy.surface.93b1554a829a9433": "3 úrovně",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Neuložené změny",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normální",
- "legacy.surface.a759d24fa1d32402": "Otevřete všechna rozhraní WebUI",
"legacy.surface.a75ee6a28c39f865": "Přetažením podřízených složek můžete ovládat pořadí zobrazené v náhledech a rozbalených řádcích.",
"legacy.surface.a76ac50faa110d7d": "Nejdříve nejstarší vytvořené složky",
"legacy.surface.a76e13b9839270eb": "Obnovit",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Suchý běh",
"legacy.surface.d640c7421da06661": "Pokračovat",
"legacy.surface.d66d27cf2aa666c8": "Zobrazit volby",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Přiveďte existující nastavení FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Blokovat vyskakovací okna",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Porovnejte",
"legacy.surface.e89b30aa1dc30a6a": "Zdroje",
"legacy.surface.e89c3109e1f14c41": "Cílová složka",
+ "legacy.surface.e8b4d99f2d672a8e": "Vlastní profily WebUI",
"legacy.surface.e8e470943fd0f0b6": "Pořadí vnořených složek",
"legacy.surface.e948f182d485afee": "Rozbalte větev",
"legacy.surface.e94d3ee06ecf6aac": "Export se nezdařil",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/docker.json
index a70032c9..85500adb 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Mask offentlige IP'er",
"docker.privacy.mask-network-interfaces": "Maske netværksgrænseflader",
"docker.privacy.mask-external-urls": "Mask eksterne URL'er",
- "docker.actions.capture-diagnostics": "Optag sidediagnostik"
+ "docker.actions.capture-diagnostics": "Optag sidediagnostik",
+ "docker.folder.hide": "Skjul mappe",
+ "docker.folder.restore": "Gendan mappe",
+ "docker.folder.restore-hidden-branch": "Gendan skjult gren",
+ "docker.folder.hidden-indicator": "Skjult",
+ "docker.folder.unnamed": "Mappe",
+ "docker.folder.visibility-failed": "Indstillingen for mappesynlighed kunne ikke gemmes.",
+ "docker.visibility.hidden-folders": "Skjulte mapper",
+ "docker.visibility.restore-all": "Gendan alle skjulte mapper",
+ "docker.visibility.hidden-title": "Mappe skjult",
+ "docker.visibility.folder-fallback": "Mappe",
+ "docker.visibility.hidden-message": "var skjult fra Docker-visningen.",
+ "docker.visibility.undo": "Fortryd",
+ "docker.visibility.all-hidden-title": "Alle mapperækker er skjulte.",
+ "docker.visibility.all-hidden-help": "Vis dem midlertidigt eller gendan hver skjult mappe.",
+ "docker.visibility.show-hidden": "Vis skjulte mapper",
+ "docker.webui-profiles.open": "Åbn WebUI-profilen",
+ "docker.webui-profiles.ready": "$1 ($2 af $3 klar)",
+ "docker.webui-profiles.manage": "Administrer WebUI-profiler",
+ "docker.webui-profiles.none-title": "Ingen WebUI'er klar",
+ "docker.webui-profiles.none-body": "Ingen af de valgte containere kører med en tilgængelig WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/editor.json
index d66fe586..f5da79fe 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Komponér projekt lig med",
"editor.rules.templates.compose-project-placeholder": "Projektnavn (eksempel: medie)",
"editor.rules.templates.label-equals": "Docker-label er lig",
- "editor.rules.templates.label-equals-placeholder": "Etiket nøgle=værdi"
+ "editor.rules.templates.label-equals-placeholder": "Etiket nøgle=værdi",
+ "editor.webui-profiles.title": "Brugerdefinerede WebUI-profiler",
+ "editor.webui-profiles.help": "Opret genbrugelige startgrupper fra denne mappes direkte Docker-medlemmer. Aktuelle WebUI-adresser og køretilstand kontrolleres kun, når en profil åbnes.",
+ "editor.webui-profiles.add": "Tilføj profil",
+ "editor.webui-profiles.count": "$1 gemte profil(er)",
+ "editor.webui-profiles.empty": "Ingen brugerdefinerede WebUI-profiler endnu.",
+ "editor.webui-profiles.name": "Profilnavn",
+ "editor.webui-profiles.duplicate": "Duplikat",
+ "editor.webui-profiles.move-up": "Flyt op",
+ "editor.webui-profiles.move-down": "Flyt ned",
+ "editor.webui-profiles.delete": "Slet",
+ "editor.webui-profiles.search": "Søg mappemedlemmer",
+ "editor.webui-profiles.select-webui": "Vælg alle med WebUI",
+ "editor.webui-profiles.clear": "Ryd markering",
+ "editor.webui-profiles.webui-ready": "WebUI konfigureret",
+ "editor.webui-profiles.webui-runtime": "WebUI kontrolleret ved lancering",
+ "editor.webui-profiles.unavailable": "Ikke tilgængelig i denne mappe",
+ "editor.webui-profiles.no-members": "Ingen matchende mappemedlemmer er tilgængelige.",
+ "editor.webui-profiles.error-name": "Hver WebUI-profil har brug for et navn.",
+ "editor.webui-profiles.error-duplicate": "WebUI-profilnavne skal være unikke i denne mappe.",
+ "editor.webui-profiles.error-members": "Hver WebUI-profil skal vælge mindst ét mappemedlem.",
+ "editor.webui-profiles.limit": "Denne mappe har nået sikkerhedsgrænsen for WebUI-profilen."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/legacy-surface.json
index 53d2e262..832cbfe2 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/da/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Ingen appændringer",
"legacy.surface.26f0360c9dddc4d1": "Forudindstilling slettet",
"legacy.surface.270bca2990500a60": "Vælg, hvor denne mappe bor i hierarkiet.",
+ "legacy.surface.273c468809bda389": "Indstil indikatorfarver og den uafhængige statustekstfarve, der bruges af mapperækker.",
"legacy.surface.273f838c039ee10f": "FolderView Plus registrerede, at siden Indstillinger blev tom, før initialiseringen var fuldført.",
"legacy.surface.2753807153755a5e": "Flet (tilføj + opdater)",
"legacy.surface.2847d4f584d08e0c": "Indlæser Docker-kommandovisning...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Start gren ($1)",
"legacy.surface.32c919087b37ec2d": "Mediestak",
"legacy.surface.334277e71d658735": "Efter målet",
+ "legacy.surface.33541731c2a77842": "Indstil indikatorfarverne Startet, Pauseret og Stoppet uafhængigt af statustekstfarven på tværs af Docker-, VM'er og Dashboard-visninger.",
"legacy.surface.3388c6d7238cdde6": "Overskrift til plugin-indstillinger",
"legacy.surface.33891d013966c572": "Vælg en mappe til massetildeling.",
"legacy.surface.33c5850b76f401e8": "Mappen på øverste niveau",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Vælg tilstandsændringen, og hvilke medlemmer den kontrollerer.",
"legacy.surface.613aa3b591bacd9a": "Samlet lagerplads (filstøttede diske): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron farver:",
+ "legacy.surface.61ea108a3718792d": "Opret navngivne grupper, der kun åbner udvalgte Docker container WebUI'er.",
"legacy.surface.61f565ba05bd647d": "Matchende container- eller VM-navne vil automatisk blive inkluderet her.",
"legacy.surface.61fcdf8c82cb687a": "Vil du anvende forhåndsviste opgaver?",
"legacy.surface.6233afa7701a7fb8": "Medtag vist",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Vrik",
"legacy.surface.7179ea0035fca783": "Ryd filtre",
"legacy.surface.7188848298a0b500": "Omfang: favoritter",
+ "legacy.surface.71988c4d8e0803ba": "Tekst",
"legacy.surface.719e8555bf1abeba": "Brug trinnet Skabeloner, importer filer, eller opret mapper manuelt først.",
"legacy.surface.71b2e8d085cdca1e": "Klar: alle kontroller bestået.",
"legacy.surface.7205aff54d71cb8b": "Kopier mappeindstillinger fra en anden Docker-mappe først.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Eksempel:",
"legacy.surface.75bf7d9afc988cb6": "Mapper er sorteret efter navn fra A til Z. Indlejrede mapper forbliver stadig i deres overordnede gren.",
"legacy.surface.75d599007fcf49af": "Alt medlemskab",
- "legacy.surface.75d7a33335c4f033": "Indstil de startede, stoppede og stoppede farver, der bruges af mapperækker.",
"legacy.surface.7611fe57a952da4b": "Anbefales. Blokerer automatisk risikofyldte ændringer.",
"legacy.surface.764210a29b968296": "Skabelonkategori",
"legacy.surface.764b74dea7f342ff": "Styrer, hvor mange indlejrede mappeniveauer, der vises som chips, når underordnede mapper vises i den skjulte forhåndsvisning.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM tabel krop",
"legacy.surface.8569ce7917f23eb6": "Vil du slette $1 valgte regler?",
"legacy.surface.858e4ba7a29fd38b": "Pause",
+ "legacy.surface.85b6c17862a72780": "WebUI-profiler",
"legacy.surface.85c83f4500bb9f2c": "VM runtime info",
"legacy.surface.85e2efe37bad1a44": "Ingen kvalificerede elementer til denne handling.",
"legacy.surface.8600edb883562f2c": "Maske LAN-IP'er",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Scan din aktuelle Docker- og VM-mappeopsætning.",
"legacy.surface.916b423aa46d7493": "Registreret $1 rådgivende problem(er) (dybde/tom gren), men ingen forældreløse/cyklus-linkfejl til autoreparation.",
"legacy.surface.91f41a1797b442ab": "Opdateret $1 fra gemt skabelon.",
- "legacy.surface.920b3e3ce5710d1d": "Indstil brugerdefinerede farver for denne mappes statusikon og tekst på tværs af Docker-, VM'er og Dashboard-visninger.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Advarselstærskel for opmærksomhed",
"legacy.surface.921b15089caf4ff8": "3. Gennemgå regelrækkefølgen",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Styrer, hvordan afventende opdateringer påvirker denne mappes tilstand.",
"legacy.surface.926e4eab47bf42ea": "Live-kampe: Indtast et regex-mønster.",
"legacy.surface.92c1cdfdf4cb9cf6": "Aktiveret",
+ "legacy.surface.92cff14b4b7992dd": "Vælg de direkte mappemedlemmer, der åbnes af hver navngivne profil.",
"legacy.surface.92ec9d61393d7064": "$1 regulært udtryk",
"legacy.surface.93aa7d6a1b93167d": "Ingen brugerdefinerede ikoner fundet. Upload et ikon for at komme i gang.",
"legacy.surface.93b1554a829a9433": "3 niveauer",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Ikke-gemte ændringer",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Åbn alle WebUI'er",
"legacy.surface.a75ee6a28c39f865": "Træk undermapper for at kontrollere rækkefølgen vist i forhåndsvisninger og udvidede rækker.",
"legacy.surface.a76ac50faa110d7d": "Ældste oprettede mapper først",
"legacy.surface.a76e13b9839270eb": "Gendan",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Tørløb",
"legacy.surface.d640c7421da06661": "Genoptag",
"legacy.surface.d66d27cf2aa666c8": "Vis valg",
+ "legacy.surface.d693881b5ac21882": "--fvplus-mappe-status-tekst",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Indsæt en eksisterende FolderView-opsætning.",
"legacy.surface.d6a226a96bdc60fe": "Bloker pop-ups",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Sammenlign",
"legacy.surface.e89b30aa1dc30a6a": "Ressourcer",
"legacy.surface.e89c3109e1f14c41": "Målmappe",
+ "legacy.surface.e8b4d99f2d672a8e": "Brugerdefinerede WebUI-profiler",
"legacy.surface.e8e470943fd0f0b6": "Indlejret mapperækkefølge",
"legacy.surface.e948f182d485afee": "Udvid gren",
"legacy.surface.e94d3ee06ecf6aac": "Eksporten mislykkedes",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/docker.json
index c6b6fa42..c31f0d7a 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Öffentlich maskieren IPs",
"docker.privacy.mask-network-interfaces": "Maskieren Sie Netzwerkschnittstellen",
"docker.privacy.mask-external-urls": "Externe URLs maskieren",
- "docker.actions.capture-diagnostics": "Erfassen Sie Seitendiagnosen"
+ "docker.actions.capture-diagnostics": "Erfassen Sie Seitendiagnosen",
+ "docker.folder.hide": "Ordner ausblenden",
+ "docker.folder.restore": "Ordner wiederherstellen",
+ "docker.folder.restore-hidden-branch": "Verborgenen Zweig wiederherstellen",
+ "docker.folder.hidden-indicator": "Versteckt",
+ "docker.folder.unnamed": "Ordner",
+ "docker.folder.visibility-failed": "Die Sichtbarkeitseinstellung für den Ordner konnte nicht gespeichert werden.",
+ "docker.visibility.hidden-folders": "Versteckte Ordner",
+ "docker.visibility.restore-all": "Alle versteckten Ordner wiederherstellen",
+ "docker.visibility.hidden-title": "Ordner ausgeblendet",
+ "docker.visibility.folder-fallback": "Ordner",
+ "docker.visibility.hidden-message": "wurde aus der Docker-Ansicht ausgeblendet.",
+ "docker.visibility.undo": "Rückgängig machen",
+ "docker.visibility.all-hidden-title": "Alle Ordnerzeilen werden ausgeblendet.",
+ "docker.visibility.all-hidden-help": "Zeigen Sie sie vorübergehend an oder stellen Sie alle versteckten Ordner wieder her.",
+ "docker.visibility.show-hidden": "Versteckte Ordner anzeigen",
+ "docker.webui-profiles.open": "Öffnen Sie das WebUI-Profil",
+ "docker.webui-profiles.ready": "$1 ($2 von $3 bereit)",
+ "docker.webui-profiles.manage": "WebUI-Profile verwalten",
+ "docker.webui-profiles.none-title": "Keine WebUIs bereit",
+ "docker.webui-profiles.none-body": "Keiner der ausgewählten Container wird mit einer verfügbaren WebUI ausgeführt."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/editor.json
index 5f6e0c64..f5736131 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Projekt erstellen ist gleich",
"editor.rules.templates.compose-project-placeholder": "Projektname (Beispiel: Medien)",
"editor.rules.templates.label-equals": "Docker-Label gleich",
- "editor.rules.templates.label-equals-placeholder": "Label-Schlüssel=Wert"
+ "editor.rules.templates.label-equals-placeholder": "Label-Schlüssel=Wert",
+ "editor.webui-profiles.title": "Benutzerdefinierte WebUI-Profile",
+ "editor.webui-profiles.help": "Erstellen Sie wiederverwendbare Startgruppen aus den direkten Docker-Mitgliedern dieses Ordners. Aktuelle WebUI-Adressen und der Ausführungsstatus werden nur überprüft, wenn ein Profil geöffnet wird.",
+ "editor.webui-profiles.add": "Profil hinzufügen",
+ "editor.webui-profiles.count": "$1 gespeicherte Profile",
+ "editor.webui-profiles.empty": "Noch keine benutzerdefinierten WebUI-Profile.",
+ "editor.webui-profiles.name": "Profilname",
+ "editor.webui-profiles.duplicate": "Duplizieren",
+ "editor.webui-profiles.move-up": "Bewegen Sie sich nach oben",
+ "editor.webui-profiles.move-down": "Bewegen Sie sich nach unten",
+ "editor.webui-profiles.delete": "Löschen",
+ "editor.webui-profiles.search": "Ordnermitglieder durchsuchen",
+ "editor.webui-profiles.select-webui": "Wählen Sie alle mit der WebUI aus",
+ "editor.webui-profiles.clear": "Klare Auswahl",
+ "editor.webui-profiles.webui-ready": "WebUI konfiguriert",
+ "editor.webui-profiles.webui-runtime": "WebUI wurde beim Start überprüft",
+ "editor.webui-profiles.unavailable": "In diesem Ordner nicht verfügbar",
+ "editor.webui-profiles.no-members": "Es sind keine passenden Ordnermitglieder verfügbar.",
+ "editor.webui-profiles.error-name": "Jedes WebUI-Profil benötigt einen Namen.",
+ "editor.webui-profiles.error-duplicate": "WebUI-Profilnamen müssen innerhalb dieses Ordners eindeutig sein.",
+ "editor.webui-profiles.error-members": "Jedes WebUI-Profil muss mindestens ein Ordnermitglied auswählen.",
+ "editor.webui-profiles.limit": "Dieser Ordner hat die Sicherheitsgrenze des WebUI-Profils erreicht."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/legacy-surface.json
index ae5d94a2..cd47c41d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/de/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Keine App-Änderungen",
"legacy.surface.26f0360c9dddc4d1": "Voreinstellung gelöscht",
"legacy.surface.270bca2990500a60": "Wählen Sie aus, wo sich dieser Ordner in der Hierarchie befindet.",
+ "legacy.surface.273c468809bda389": "Legen Sie die Indikatorfarben und die unabhängige Statustextfarbe fest, die von Ordnerzeilen verwendet wird.",
"legacy.surface.273f838c039ee10f": "FolderView Plus hat festgestellt, dass die Seite „Einstellungen“ leer war, bevor die Initialisierung abgeschlossen war.",
"legacy.surface.2753807153755a5e": "Zusammenführen (Hinzufügen + Aktualisieren)",
"legacy.surface.2847d4f584d08e0c": "Docker-Befehlsansicht wird geladen...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Zweig starten ($1)",
"legacy.surface.32c919087b37ec2d": "Medienstapel",
"legacy.surface.334277e71d658735": "Nach dem Ziel",
+ "legacy.surface.33541731c2a77842": "Legen Sie die Farben der Indikatoren „Gestartet“, „Pause“ und „Gestoppt“ unabhängig von der Statustextfarbe für Docker-, VMs- und Dashboard-Ansichten fest.",
"legacy.surface.3388c6d7238cdde6": "Titel der Plugin-Einstellungen",
"legacy.surface.33891d013966c572": "Wählen Sie einen Ordner für die Massenzuweisung aus.",
"legacy.surface.33c5850b76f401e8": "Ordner der obersten Ebene",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Wählen Sie die Statusänderung und die Mitglieder aus, die sie steuert.",
"legacy.surface.613aa3b591bacd9a": "Gesamtspeicher (dateigestützte Festplatten): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron-Farben:",
+ "legacy.surface.61ea108a3718792d": "Erstellen Sie benannte Gruppen, die nur ausgewählte Docker-Container-WebUIs öffnen.",
"legacy.surface.61f565ba05bd647d": "Passende Container- oder VM-Namen werden hier automatisch eingefügt.",
"legacy.surface.61fcdf8c82cb687a": "Vorschauaufgaben anwenden?",
"legacy.surface.6233afa7701a7fb8": "Angezeigt einschließen",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Wackeln",
"legacy.surface.7179ea0035fca783": "Filter löschen",
"legacy.surface.7188848298a0b500": "Geltungsbereich: Favoriten",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.719e8555bf1abeba": "Verwenden Sie zunächst den Schritt „Vorlagen“, importieren Sie Dateien oder erstellen Sie Ordner manuell.",
"legacy.surface.71b2e8d085cdca1e": "Fertig: Alle Prüfungen bestanden.",
"legacy.surface.7205aff54d71cb8b": "Kopieren Sie zunächst die Ordnereinstellungen aus einem anderen Docker-Ordner.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Vorschau:",
"legacy.surface.75bf7d9afc988cb6": "Ordner werden nach Namen von A bis Z sortiert. Verschachtelte Ordner bleiben weiterhin in ihrem übergeordneten Zweig.",
"legacy.surface.75d599007fcf49af": "Alle Mitgliedschaft",
- "legacy.surface.75d7a33335c4f033": "Legen Sie die von Ordnerzeilen verwendeten Start-, Pausen- und Stoppfarben fest.",
"legacy.surface.7611fe57a952da4b": "Empfohlen. Blockiert riskante Änderungen automatisch.",
"legacy.surface.764210a29b968296": "Vorlagenkategorie",
"legacy.surface.764b74dea7f342ff": "Steuert, wie viele verschachtelte Ordnerebenen als Chips angezeigt werden, wenn untergeordnete Ordner in der reduzierten Vorschau angezeigt werden.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM-Tabellenkörper",
"legacy.surface.8569ce7917f23eb6": "$1 ausgewählte(n) Regel(n) löschen?",
"legacy.surface.858e4ba7a29fd38b": "Pause",
+ "legacy.surface.85b6c17862a72780": "WebUI-Profile",
"legacy.surface.85c83f4500bb9f2c": "Informationen zur VM-Laufzeit",
"legacy.surface.85e2efe37bad1a44": "Für diese Aktion sind keine geeigneten Artikel vorhanden.",
"legacy.surface.8600edb883562f2c": "Maskieren Sie LAN-IPs",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Scannen Sie Ihr aktuelles Docker- und VM-Ordner-Setup.",
"legacy.surface.916b423aa46d7493": "Es wurden $1-Beratungsprobleme erkannt (Tiefe/leerer Zweig), aber keine Waisen-/Zyklus-Link-Fehler zur automatischen Reparatur.",
"legacy.surface.91f41a1797b442ab": "$1 aus der gespeicherten Vorlage aktualisiert.",
- "legacy.surface.920b3e3ce5710d1d": "Legen Sie benutzerdefinierte Farben für das Statussymbol und den Text dieses Ordners in den Docker-, VMs- und Dashboard-Ansichten fest.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Warnschwelle für Aufmerksamkeit",
"legacy.surface.921b15089caf4ff8": "3. Überprüfen Sie die Regelreihenfolge",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Steuert, wie sich ausstehende Updates auf den Zustand dieses Ordners auswirken.",
"legacy.surface.926e4eab47bf42ea": "Live-Matches: Geben Sie ein Regex-Muster ein.",
"legacy.surface.92c1cdfdf4cb9cf6": "Aktiviert",
+ "legacy.surface.92cff14b4b7992dd": "Wählen Sie die direkten Ordnermitglieder aus, die von jedem benannten Profil geöffnet werden.",
"legacy.surface.92ec9d61393d7064": "$1 regulärer Ausdruck",
"legacy.surface.93aa7d6a1b93167d": "Keine benutzerdefinierten Symbole gefunden. Laden Sie ein Symbol hoch, um loszulegen.",
"legacy.surface.93b1554a829a9433": "3 Ebenen",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Nicht gespeicherte Änderungen",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Öffnen Sie alle WebUIs",
"legacy.surface.a75ee6a28c39f865": "Ziehen Sie untergeordnete Ordner, um die in Vorschauen und erweiterten Zeilen angezeigte Reihenfolge zu steuern.",
"legacy.surface.a76ac50faa110d7d": "Die ältesten erstellten Ordner zuerst",
"legacy.surface.a76e13b9839270eb": "Wiederherstellen",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Trockenlauf",
"legacy.surface.d640c7421da06661": "Lebenslauf",
"legacy.surface.d66d27cf2aa666c8": "Anzeigeoptionen",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Bringen Sie ein vorhandenes FolderView-Setup ein.",
"legacy.surface.d6a226a96bdc60fe": "Pop-ups blockieren",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Vergleichen",
"legacy.surface.e89b30aa1dc30a6a": "Ressourcen",
"legacy.surface.e89c3109e1f14c41": "Zielordner",
+ "legacy.surface.e8b4d99f2d672a8e": "Benutzerdefinierte WebUI-Profile",
"legacy.surface.e8e470943fd0f0b6": "Reihenfolge der verschachtelten Ordner",
"legacy.surface.e948f182d485afee": "Zweig erweitern",
"legacy.surface.e94d3ee06ecf6aac": "Der Export ist fehlgeschlagen",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/docker.json
index 0e21f9ee..ba6a6268 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Mask public IPs",
"docker.privacy.mask-network-interfaces": "Mask network interfaces",
"docker.privacy.mask-external-urls": "Mask external URLs",
- "docker.actions.capture-diagnostics": "Capture page diagnostics"
+ "docker.actions.capture-diagnostics": "Capture page diagnostics",
+ "docker.folder.hide": "Hide folder",
+ "docker.folder.restore": "Restore folder",
+ "docker.folder.restore-hidden-branch": "Restore hidden branch",
+ "docker.folder.hidden-indicator": "Hidden",
+ "docker.folder.unnamed": "Folder",
+ "docker.folder.visibility-failed": "The folder visibility preference could not be saved.",
+ "docker.visibility.hidden-folders": "Hidden folders",
+ "docker.visibility.restore-all": "Restore all hidden folders",
+ "docker.visibility.hidden-title": "Folder hidden",
+ "docker.visibility.folder-fallback": "Folder",
+ "docker.visibility.hidden-message": "was hidden from the Docker view.",
+ "docker.visibility.undo": "Undo",
+ "docker.visibility.all-hidden-title": "All folder rows are hidden.",
+ "docker.visibility.all-hidden-help": "Reveal them temporarily or restore every hidden folder.",
+ "docker.visibility.show-hidden": "Show hidden folders",
+ "docker.webui-profiles.open": "Open WebUI profile",
+ "docker.webui-profiles.ready": "$1 ($2 of $3 ready)",
+ "docker.webui-profiles.manage": "Manage WebUI profiles",
+ "docker.webui-profiles.none-title": "No WebUIs ready",
+ "docker.webui-profiles.none-body": "None of the selected containers are running with an available WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/editor.json
index cfdf8e20..c85ad2aa 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Compose project equals",
"editor.rules.templates.compose-project-placeholder": "Project name (example: media)",
"editor.rules.templates.label-equals": "Docker label equals",
- "editor.rules.templates.label-equals-placeholder": "Label key=value"
+ "editor.rules.templates.label-equals-placeholder": "Label key=value",
+ "editor.webui-profiles.title": "Custom WebUI profiles",
+ "editor.webui-profiles.help": "Create reusable launch groups from this folder's direct Docker members. Current WebUI addresses and running state are checked only when a profile is opened.",
+ "editor.webui-profiles.add": "Add profile",
+ "editor.webui-profiles.count": "$1 saved profile(s)",
+ "editor.webui-profiles.empty": "No custom WebUI profiles yet.",
+ "editor.webui-profiles.name": "Profile name",
+ "editor.webui-profiles.duplicate": "Duplicate",
+ "editor.webui-profiles.move-up": "Move up",
+ "editor.webui-profiles.move-down": "Move down",
+ "editor.webui-profiles.delete": "Delete",
+ "editor.webui-profiles.search": "Search folder members",
+ "editor.webui-profiles.select-webui": "Select all with WebUI",
+ "editor.webui-profiles.clear": "Clear selection",
+ "editor.webui-profiles.webui-ready": "WebUI configured",
+ "editor.webui-profiles.webui-runtime": "WebUI checked at launch",
+ "editor.webui-profiles.unavailable": "Unavailable in this folder",
+ "editor.webui-profiles.no-members": "No matching folder members are available.",
+ "editor.webui-profiles.error-name": "Every WebUI profile needs a name.",
+ "editor.webui-profiles.error-duplicate": "WebUI profile names must be unique within this folder.",
+ "editor.webui-profiles.error-members": "Every WebUI profile must select at least one folder member.",
+ "editor.webui-profiles.limit": "This folder has reached the WebUI profile safety limit."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/legacy-surface.json
index cf6a7a0b..294f7cfd 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/en/legacy-surface.json
@@ -4,6 +4,7 @@
"locale": "en",
"namespace": "legacy-surface"
},
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.190b30fb79901790": "', targetHtml, '",
"legacy.surface.abf9bf761d7ab3c4": "\"$1\" is not used by any folder.",
"legacy.surface.4495cb650ccf4288": "(Create a folder first)",
@@ -337,6 +338,7 @@
"legacy.surface.dd7814b11ac52481": "Create at least one backup snapshot first.",
"legacy.surface.699204f7cf975f66": "Create at least one folder first, then assign items here.",
"legacy.surface.f2d80f2e77505bc0": "Create managed include or exclude rules without relying on the legacy folder regex.",
+ "legacy.surface.61ea108a3718792d": "Create named groups that open only selected Docker container WebUIs.",
"legacy.surface.7336697956ec839b": "Create one from an existing folder to reuse icon, settings, actions, and matching logic faster.",
"legacy.surface.0c7b4af5cf5ca506": "Create plugin-wide include or exclude rules that target this folder.",
"legacy.surface.9bedb4825d620a45": "Create selected",
@@ -356,6 +358,7 @@
"legacy.surface.494ca78f7374e46f": "Custom",
"legacy.surface.f7bbb5602cd6dcf3": "Custom (unsaved)",
"legacy.surface.9d3b9a0e78eaa728": "Custom CSS",
+ "legacy.surface.e8b4d99f2d672a8e": "Custom WebUI Profiles",
"legacy.surface.70f70c881ecd73ee": "Customization layer",
"legacy.surface.765d699957990120": "Customize folder views, defaults, and visibility.",
"legacy.surface.67b696468610b879": "Dashboard",
@@ -928,7 +931,6 @@
"legacy.surface.34deb4eba99d2f36": "Only $1 currently available to this folder are listed.",
"legacy.surface.00e32ea2ab786253": "Only icon",
"legacy.surface.2360b3431f8550a4": "Only members currently available to this folder are listed.",
- "legacy.surface.a759d24fa1d32402": "Open all WebUIs",
"legacy.surface.ea9a3b1d3b52f525": "Open full Rules workspace",
"legacy.surface.a61d4689dae96946": "Open health details",
"legacy.surface.2faa63295b1de460": "Open Plugins",
@@ -1257,6 +1259,7 @@
"legacy.surface.41afe0e7041c05e6": "Select none",
"legacy.surface.f2b5d8045c543f5d": "Select one or more items to preview folder moves.",
"legacy.surface.94041e93ad4b1b35": "Select one or more smart suggestions first.",
+ "legacy.surface.92cff14b4b7992dd": "Select the direct folder members opened by each named profile.",
"legacy.surface.ae58e3217839ceda": "Select the items you want to move. The plan updates live below.",
"legacy.surface.42da61bd7e205763": "Select the VMs you want to move. The plan updates live below.",
"legacy.surface.812033fe0c34e8ba": "Select two different targets to compare.",
@@ -1268,10 +1271,10 @@
"legacy.surface.329bf93cb70a5c53": "Sends matches to:",
"legacy.surface.aef7de28d52977f1": "Server",
"legacy.surface.4744d656512dd6d3": "Server environment",
- "legacy.surface.920b3e3ce5710d1d": "Set custom colors for this folder's status icon and text across Docker, VMs, and Dashboard views.",
+ "legacy.surface.273c468809bda389": "Set indicator colors and the independent status text color used by folder rows.",
"legacy.surface.fb5ae717d5a08d70": "Set the priority order that decides where new containers go.",
"legacy.surface.3b50378ee812a3f5": "Set the priority order that decides where new VMs go.",
- "legacy.surface.75d7a33335c4f033": "Set the started, paused, and stopped colors used by folder rows.",
+ "legacy.surface.33541731c2a77842": "Set the Started, Paused, and Stopped indicator colors independently from the status text color across Docker, VMs, and Dashboard views.",
"legacy.surface.74a883a037bc227f": "Settings",
"legacy.surface.03f9483a61a7715f": "Settings loaded in degraded mode",
"legacy.surface.f85b980a9329d847": "Settings page is blank",
@@ -1397,6 +1400,7 @@
"legacy.surface.aa9d83a651db514f": "Test label value (optional)",
"legacy.surface.2a15b08444492786": "Test the current rule set",
"legacy.surface.f27310598a5421a8": "Test VM name (example: prod-db)",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.57d6fa77c9199070": "The current scope only contains regex-controlled members. Edit the folder rules instead of using bulk move for those entries.",
"legacy.surface.da1f35fb8d4627bf": "The diagnostic attempt expired. Run the export again, then report it if no file appears.",
"legacy.surface.30d44908bbaa7b02": "The editor runtime will keep this preview in sync as settings change.",
@@ -1560,6 +1564,7 @@
"legacy.surface.98d2724b30eb95dd": "Warning: $1 recommendation$2 available.",
"legacy.surface.0e04cd10f9afd7e5": "Warnings",
"legacy.surface.2a240977f49a77bc": "WebUI",
+ "legacy.surface.85b6c17862a72780": "WebUI Profiles",
"legacy.surface.1bcbf1dcad4ffe49": "What changed",
"legacy.surface.5ac50496f8d7a6e7": "What FolderView Plus can do",
"legacy.surface.fecc2b5f8968b336": "What is protected",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/docker.json
index 2960ce97..8e68f393 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Enmascarar IPs público",
"docker.privacy.mask-network-interfaces": "Enmascarar interfaces de red",
"docker.privacy.mask-external-urls": "Enmascarar URLs externas",
- "docker.actions.capture-diagnostics": "Capturar diagnóstico de página"
+ "docker.actions.capture-diagnostics": "Capturar diagnóstico de página",
+ "docker.folder.hide": "Ocultar carpeta",
+ "docker.folder.restore": "Restaurar carpeta",
+ "docker.folder.restore-hidden-branch": "Restaurar rama oculta",
+ "docker.folder.hidden-indicator": "Oculto",
+ "docker.folder.unnamed": "Carpeta",
+ "docker.folder.visibility-failed": "No se pudo guardar la preferencia de visibilidad de la carpeta.",
+ "docker.visibility.hidden-folders": "Carpetas ocultas",
+ "docker.visibility.restore-all": "Restaurar todas las carpetas ocultas",
+ "docker.visibility.hidden-title": "Carpeta oculta",
+ "docker.visibility.folder-fallback": "Carpeta",
+ "docker.visibility.hidden-message": "estaba oculto de la vista de Docker.",
+ "docker.visibility.undo": "Deshacer",
+ "docker.visibility.all-hidden-title": "Todas las filas de carpetas están ocultas.",
+ "docker.visibility.all-hidden-help": "Revélelos temporalmente o restaure todas las carpetas ocultas.",
+ "docker.visibility.show-hidden": "Mostrar carpetas ocultas",
+ "docker.webui-profiles.open": "Abrir perfil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 de $3 listo)",
+ "docker.webui-profiles.manage": "Administrar perfiles de WebUI",
+ "docker.webui-profiles.none-title": "No hay UI web listas",
+ "docker.webui-profiles.none-body": "Ninguno de los contenedores seleccionados se ejecuta con una WebUI disponible."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/editor.json
index 8889cf22..ed9182c2 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Redactar proyecto es igual",
"editor.rules.templates.compose-project-placeholder": "Nombre del proyecto (ejemplo: medios)",
"editor.rules.templates.label-equals": "La etiqueta Docker es igual",
- "editor.rules.templates.label-equals-placeholder": "Clave de etiqueta = valor"
+ "editor.rules.templates.label-equals-placeholder": "Clave de etiqueta = valor",
+ "editor.webui-profiles.title": "Perfiles de WebUI personalizados",
+ "editor.webui-profiles.help": "Cree grupos de lanzamiento reutilizables a partir de los miembros directos de Docker de esta carpeta. Las direcciones actuales de WebUI y el estado de ejecución se verifican solo cuando se abre un perfil.",
+ "editor.webui-profiles.add": "Agregar perfil",
+ "editor.webui-profiles.count": "$1 perfiles guardados",
+ "editor.webui-profiles.empty": "Aún no hay perfiles WebUI personalizados.",
+ "editor.webui-profiles.name": "Nombre del perfil",
+ "editor.webui-profiles.duplicate": "Duplicar",
+ "editor.webui-profiles.move-up": "Subir",
+ "editor.webui-profiles.move-down": "Bajar",
+ "editor.webui-profiles.delete": "Eliminar",
+ "editor.webui-profiles.search": "Buscar miembros de la carpeta",
+ "editor.webui-profiles.select-webui": "Seleccionar todo con WebUI",
+ "editor.webui-profiles.clear": "Borrar selección",
+ "editor.webui-profiles.webui-ready": "WebUI configurada",
+ "editor.webui-profiles.webui-runtime": "WebUI comprobada en el lanzamiento",
+ "editor.webui-profiles.unavailable": "No disponible en esta carpeta",
+ "editor.webui-profiles.no-members": "No hay miembros de carpeta que coincidan disponibles.",
+ "editor.webui-profiles.error-name": "Cada perfil WebUI necesita un nombre.",
+ "editor.webui-profiles.error-duplicate": "Los nombres de los perfiles de WebUI deben ser únicos dentro de esta carpeta.",
+ "editor.webui-profiles.error-members": "Cada perfil de WebUI debe seleccionar al menos un miembro de la carpeta.",
+ "editor.webui-profiles.limit": "Esta carpeta ha alcanzado el límite de seguridad del perfil WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/legacy-surface.json
index a01dab68..5d3125e4 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/es/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Sin cambios en la aplicación",
"legacy.surface.26f0360c9dddc4d1": "Preestablecido eliminado",
"legacy.surface.270bca2990500a60": "Elija dónde se encuentra esta carpeta en la jerarquía.",
+ "legacy.surface.273c468809bda389": "Establezca los colores de los indicadores y el color del texto de estado independiente utilizado por las filas de la carpeta.",
"legacy.surface.273f838c039ee10f": "FolderView Plus detectó que la página de Configuración quedó en blanco antes de que se completara la inicialización.",
"legacy.surface.2753807153755a5e": "Fusionar (agregar + actualizar)",
"legacy.surface.2847d4f584d08e0c": "Cargando la vista de comandos de Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Sucursal inicial ($1)",
"legacy.surface.32c919087b37ec2d": "Pila de medios",
"legacy.surface.334277e71d658735": "Después del objetivo",
+ "legacy.surface.33541731c2a77842": "Establezca los colores de los indicadores Iniciado, En pausa y Detenido independientemente del color del texto de estado en las vistas de Docker, VM y Panel.",
"legacy.surface.3388c6d7238cdde6": "Título de configuración del complemento",
"legacy.surface.33891d013966c572": "Seleccione una carpeta para la asignación masiva.",
"legacy.surface.33c5850b76f401e8": "Carpeta de nivel superior",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Elija el cambio de estado y qué miembros controla.",
"legacy.surface.613aa3b591bacd9a": "Total de almacenamiento (discos respaldados por archivos): $1",
"legacy.surface.61c56bacd53c2dfa": "Colores de chevrón:",
+ "legacy.surface.61ea108a3718792d": "Cree grupos con nombre que abran solo las WebUI del contenedor Docker seleccionadas.",
"legacy.surface.61f565ba05bd647d": "Los nombres de contenedores o VM coincidentes se incluirán automáticamente aquí.",
"legacy.surface.61fcdf8c82cb687a": "¿Aplicar tareas previsualizadas?",
"legacy.surface.6233afa7701a7fb8": "Incluir mostrado",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Menear",
"legacy.surface.7179ea0035fca783": "Limpiar filtros",
"legacy.surface.7188848298a0b500": "Alcance: favoritos",
+ "legacy.surface.71988c4d8e0803ba": "Texto",
"legacy.surface.719e8555bf1abeba": "Utilice el paso Plantillas, importe archivos o cree carpetas manualmente primero.",
"legacy.surface.71b2e8d085cdca1e": "Listo: todos los controles pasaron.",
"legacy.surface.7205aff54d71cb8b": "Primero copie la configuración de la carpeta de otra carpeta Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Vista previa:",
"legacy.surface.75bf7d9afc988cb6": "Las carpetas se ordenan por nombre de la A a la Z. Las carpetas anidadas aún permanecen dentro de su rama principal.",
"legacy.surface.75d599007fcf49af": "Toda la membresía",
- "legacy.surface.75d7a33335c4f033": "Establezca los colores de inicio, pausa y detención utilizados por las filas de la carpeta.",
"legacy.surface.7611fe57a952da4b": "Recomendado. Bloquea los cambios riesgosos automáticamente.",
"legacy.surface.764210a29b968296": "Categoría de plantilla",
"legacy.surface.764b74dea7f342ff": "Controla cuántos niveles de carpetas anidadas aparecen como chips cuando las carpetas secundarias se muestran en la vista previa contraída.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "cuerpo de la tabla VM",
"legacy.surface.8569ce7917f23eb6": "¿Eliminar $1 regla(s) seleccionada(s)?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "Perfiles de interfaz de usuario web",
"legacy.surface.85c83f4500bb9f2c": "Información del tiempo de ejecución de la máquina virtual",
"legacy.surface.85e2efe37bad1a44": "No hay artículos elegibles para esta acción.",
"legacy.surface.8600edb883562f2c": "Enmascarar IP de LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Escanee la configuración actual de su carpeta Docker y VM.",
"legacy.surface.916b423aa46d7493": "Se detectaron problemas de aviso de $1 (profundidad/rama vacía), pero no se detectaron errores de enlace huérfano/de ciclo para la reparación automática.",
"legacy.surface.91f41a1797b442ab": "Actualizado $1 de la plantilla guardada.",
- "legacy.surface.920b3e3ce5710d1d": "Establezca colores personalizados para el icono de estado y el texto de esta carpeta en las vistas de Docker, VM y Dashboard.",
"legacy.surface.920e413c7d411b61": "Estado",
"legacy.surface.9213c7f4046c085d": "Umbral de advertencia para atención",
"legacy.surface.921b15089caf4ff8": "3. Revisar el orden de las reglas",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controla cómo las actualizaciones pendientes afectan la salud de esta carpeta.",
"legacy.surface.926e4eab47bf42ea": "Partidos en vivo: ingresa un patrón de expresión regular.",
"legacy.surface.92c1cdfdf4cb9cf6": "Habilitado",
+ "legacy.surface.92cff14b4b7992dd": "Seleccione los miembros de la carpeta directa abiertos por cada perfil nombrado.",
"legacy.surface.92ec9d61393d7064": "$1 expresión regular",
"legacy.surface.93aa7d6a1b93167d": "No se encontraron íconos personalizados. Cargue un ícono para comenzar.",
"legacy.surface.93b1554a829a9433": "3 niveles",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Cambios no guardados",
"legacy.surface.a71d508e50432f40": "Laboratorio casero",
"legacy.surface.a7248eeb45eb1ce2": "normales",
- "legacy.surface.a759d24fa1d32402": "Abrir todas las WebUI",
"legacy.surface.a75ee6a28c39f865": "Arrastre las carpetas secundarias para controlar el orden que se muestra en las vistas previas y las filas expandidas.",
"legacy.surface.a76ac50faa110d7d": "Primero las carpetas creadas más antiguas",
"legacy.surface.a76e13b9839270eb": "Restaurar",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Ejecución en seco",
"legacy.surface.d640c7421da06661": "Currículum",
"legacy.surface.d66d27cf2aa666c8": "Opciones de visualización",
+ "legacy.surface.d693881b5ac21882": "--fvplus-carpeta-estado-texto",
"legacy.surface.d696a35bdd1883da": "Perfil",
"legacy.surface.d69b03bf0620f859": "Incorpore una configuración de FolderView existente.",
"legacy.surface.d6a226a96bdc60fe": "Bloquear ventanas emergentes",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Comparar",
"legacy.surface.e89b30aa1dc30a6a": "Recursos",
"legacy.surface.e89c3109e1f14c41": "Carpeta de destino",
+ "legacy.surface.e8b4d99f2d672a8e": "Perfiles de interfaz de usuario web personalizados",
"legacy.surface.e8e470943fd0f0b6": "Orden de carpetas anidadas",
"legacy.surface.e948f182d485afee": "Expandir sucursal",
"legacy.surface.e94d3ee06ecf6aac": "Exportación fallida",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/docker.json
index 2bc69f68..4833667b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Masquer le IPs public",
"docker.privacy.mask-network-interfaces": "Masquer les interfaces réseau",
"docker.privacy.mask-external-urls": "Masquer les URL externes",
- "docker.actions.capture-diagnostics": "Diagnostic de la page de capture"
+ "docker.actions.capture-diagnostics": "Diagnostic de la page de capture",
+ "docker.folder.hide": "Masquer le dossier",
+ "docker.folder.restore": "Restaurer le dossier",
+ "docker.folder.restore-hidden-branch": "Restaurer la branche cachée",
+ "docker.folder.hidden-indicator": "Caché",
+ "docker.folder.unnamed": "Dossier",
+ "docker.folder.visibility-failed": "La préférence de visibilité du dossier n'a pas pu être enregistrée.",
+ "docker.visibility.hidden-folders": "Dossiers cachés",
+ "docker.visibility.restore-all": "Restaurer tous les dossiers cachés",
+ "docker.visibility.hidden-title": "Dossier caché",
+ "docker.visibility.folder-fallback": "Dossier",
+ "docker.visibility.hidden-message": "était masqué de la vue Docker.",
+ "docker.visibility.undo": "Annuler",
+ "docker.visibility.all-hidden-title": "Toutes les lignes de dossiers sont masquées.",
+ "docker.visibility.all-hidden-help": "Révélez-les temporairement ou restaurez chaque dossier caché.",
+ "docker.visibility.show-hidden": "Afficher les dossiers cachés",
+ "docker.webui-profiles.open": "Ouvrir le profil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 sur $3 prêt)",
+ "docker.webui-profiles.manage": "Gérer les profils WebUI",
+ "docker.webui-profiles.none-title": "Aucune interface Web n'est prête",
+ "docker.webui-profiles.none-body": "Aucun des conteneurs sélectionnés ne s'exécute avec une interface Web disponible."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/editor.json
index 8531a3a2..10499d7d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Composer un projet est égal à",
"editor.rules.templates.compose-project-placeholder": "Nom du projet (exemple : média)",
"editor.rules.templates.label-equals": "L'étiquette Docker est égale à",
- "editor.rules.templates.label-equals-placeholder": "Clé d'étiquette = valeur"
+ "editor.rules.templates.label-equals-placeholder": "Clé d'étiquette = valeur",
+ "editor.webui-profiles.title": "Profils WebUI personnalisés",
+ "editor.webui-profiles.help": "Créez des groupes de lancement réutilisables à partir des membres Docker directs de ce dossier. Les adresses WebUI actuelles et l’état d’exécution sont vérifiés uniquement lorsqu’un profil est ouvert.",
+ "editor.webui-profiles.add": "Ajouter un profil",
+ "editor.webui-profiles.count": "$1 profil(s) enregistré(s)",
+ "editor.webui-profiles.empty": "Aucun profil WebUI personnalisé pour l'instant.",
+ "editor.webui-profiles.name": "Nom du profil",
+ "editor.webui-profiles.duplicate": "Dupliquer",
+ "editor.webui-profiles.move-up": "Monter",
+ "editor.webui-profiles.move-down": "Descendre",
+ "editor.webui-profiles.delete": "Supprimer",
+ "editor.webui-profiles.search": "Rechercher des membres de dossier",
+ "editor.webui-profiles.select-webui": "Tout sélectionner avec WebUI",
+ "editor.webui-profiles.clear": "Effacer la sélection",
+ "editor.webui-profiles.webui-ready": "Interface utilisateur Web configurée",
+ "editor.webui-profiles.webui-runtime": "WebUI vérifié au lancement",
+ "editor.webui-profiles.unavailable": "Indisponible dans ce dossier",
+ "editor.webui-profiles.no-members": "Aucun membre de dossier correspondant n’est disponible.",
+ "editor.webui-profiles.error-name": "Chaque profil WebUI a besoin d'un nom.",
+ "editor.webui-profiles.error-duplicate": "Les noms de profil WebUI doivent être uniques dans ce dossier.",
+ "editor.webui-profiles.error-members": "Chaque profil WebUI doit sélectionner au moins un membre de dossier.",
+ "editor.webui-profiles.limit": "Ce dossier a atteint la limite de sécurité du profil WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/legacy-surface.json
index 8b22ca3e..e0dad431 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/fr/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Aucun changement d'application",
"legacy.surface.26f0360c9dddc4d1": "Préréglage supprimé",
"legacy.surface.270bca2990500a60": "Choisissez où se trouve ce dossier dans la hiérarchie.",
+ "legacy.surface.273c468809bda389": "Définissez les couleurs des indicateurs et la couleur du texte d’état indépendant utilisée par les lignes du dossier.",
"legacy.surface.273f838c039ee10f": "FolderView Plus a détecté que la page Paramètres était devenue vide avant la fin de l'initialisation.",
"legacy.surface.2753807153755a5e": "Fusionner (ajouter + mettre à jour)",
"legacy.surface.2847d4f584d08e0c": "Chargement de la vue des commandes Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Démarrer la branche ($1)",
"legacy.surface.32c919087b37ec2d": "Pile multimédia",
"legacy.surface.334277e71d658735": "Après la cible",
+ "legacy.surface.33541731c2a77842": "Définissez les couleurs des indicateurs Démarré, Pause et Arrêté indépendamment de la couleur du texte d'état dans les vues Docker, VM et Tableau de bord.",
"legacy.surface.3388c6d7238cdde6": "Titre des paramètres du plugin",
"legacy.surface.33891d013966c572": "Sélectionnez un dossier pour l'attribution groupée.",
"legacy.surface.33c5850b76f401e8": "Dossier de niveau supérieur",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Choisissez le changement d'état et les membres qu'il contrôle.",
"legacy.surface.613aa3b591bacd9a": "Total de stockage (disques sauvegardés sur fichiers) : $1",
"legacy.surface.61c56bacd53c2dfa": "Couleurs des chevrons :",
+ "legacy.surface.61ea108a3718792d": "Créez des groupes nommés qui ouvrent uniquement les interfaces Web du conteneur Docker sélectionnées.",
"legacy.surface.61f565ba05bd647d": "Les noms de conteneurs ou de machines virtuelles correspondants seront automatiquement inclus ici.",
"legacy.surface.61fcdf8c82cb687a": "Appliquer les devoirs prévisualisés ?",
"legacy.surface.6233afa7701a7fb8": "Inclure affiché",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Se tortiller",
"legacy.surface.7179ea0035fca783": "Effacer les filtres",
"legacy.surface.7188848298a0b500": "Portée : favoris",
+ "legacy.surface.71988c4d8e0803ba": "Texte",
"legacy.surface.719e8555bf1abeba": "Utilisez l’étape Modèles, importez des fichiers ou créez d’abord des dossiers manuellement.",
"legacy.surface.71b2e8d085cdca1e": "Prêt : tous les contrôles ont été réussis.",
"legacy.surface.7205aff54d71cb8b": "Copiez d'abord les paramètres du dossier à partir d'un autre dossier Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Aperçu :",
"legacy.surface.75bf7d9afc988cb6": "Les dossiers sont triés par nom de A à Z. Les dossiers imbriqués restent toujours dans leur branche parent.",
"legacy.surface.75d599007fcf49af": "Tous les membres",
- "legacy.surface.75d7a33335c4f033": "Définissez les couleurs de démarrage, de pause et d'arrêt utilisées par les lignes de dossiers.",
"legacy.surface.7611fe57a952da4b": "Recommandé. Bloque automatiquement les modifications à risque.",
"legacy.surface.764210a29b968296": "Catégorie de modèle",
"legacy.surface.764b74dea7f342ff": "Contrôle le nombre de niveaux de dossiers imbriqués qui apparaissent sous forme de puces lorsque les dossiers enfants sont affichés dans l'aperçu réduit.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Corps de la table de la machine virtuelle",
"legacy.surface.8569ce7917f23eb6": "Supprimer $1 règle(s) sélectionnée(s) ?",
"legacy.surface.858e4ba7a29fd38b": "Pause",
+ "legacy.surface.85b6c17862a72780": "Profils d'interface utilisateur Web",
"legacy.surface.85c83f4500bb9f2c": "Informations sur l'exécution de la VM",
"legacy.surface.85e2efe37bad1a44": "Aucun article éligible pour cette action.",
"legacy.surface.8600edb883562f2c": "Masquer les IP LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Analysez la configuration actuelle de votre Docker et de votre dossier VM.",
"legacy.surface.916b423aa46d7493": "Problème(s) consultatif(s) $1 détecté(s) (profondeur/branche vide), mais aucune erreur de lien orphelin/cycle à réparer automatiquement.",
"legacy.surface.91f41a1797b442ab": "Mise à jour de $1 à partir du modèle enregistré.",
- "legacy.surface.920b3e3ce5710d1d": "Définissez des couleurs personnalisées pour l'icône d'état et le texte de ce dossier dans les vues Docker, VM et Tableau de bord.",
"legacy.surface.920e413c7d411b61": "Statut",
"legacy.surface.9213c7f4046c085d": "Seuil d'avertissement pour l'attention",
"legacy.surface.921b15089caf4ff8": "3. Vérifier l'ordre des règles",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Contrôle la façon dont les mises à jour en attente affectent la santé de ce dossier.",
"legacy.surface.926e4eab47bf42ea": "Matchs en direct : saisissez un modèle d'expression régulière.",
"legacy.surface.92c1cdfdf4cb9cf6": "Activé",
+ "legacy.surface.92cff14b4b7992dd": "Sélectionnez les membres du dossier direct ouverts par chaque profil nommé.",
"legacy.surface.92ec9d61393d7064": "$1 expression régulière",
"legacy.surface.93aa7d6a1b93167d": "Aucune icône personnalisée trouvée. Téléchargez une icône pour commencer.",
"legacy.surface.93b1554a829a9433": "3 niveaux",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Modifications non enregistrées",
"legacy.surface.a71d508e50432f40": "Laboratoire à domicile",
"legacy.surface.a7248eeb45eb1ce2": "Normale",
- "legacy.surface.a759d24fa1d32402": "Ouvrir toutes les interfaces Web",
"legacy.surface.a75ee6a28c39f865": "Faites glisser les dossiers enfants pour contrôler l’ordre affiché dans les aperçus et les lignes développées.",
"legacy.surface.a76ac50faa110d7d": "Les dossiers créés les plus anciens en premier",
"legacy.surface.a76e13b9839270eb": "Restaurer",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Essai à sec",
"legacy.surface.d640c7421da06661": "CV",
"legacy.surface.d66d27cf2aa666c8": "Choix d'affichage",
+ "legacy.surface.d693881b5ac21882": "--fvplus-dossier-statut-texte",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Apportez une configuration FolderView existante.",
"legacy.surface.d6a226a96bdc60fe": "Bloquer les pop-ups",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Comparez",
"legacy.surface.e89b30aa1dc30a6a": "Ressources",
"legacy.surface.e89c3109e1f14c41": "Dossier cible",
+ "legacy.surface.e8b4d99f2d672a8e": "Profils WebUI personnalisés",
"legacy.surface.e8e470943fd0f0b6": "Ordre des dossiers imbriqués",
"legacy.surface.e948f182d485afee": "Développer la branche",
"legacy.surface.e94d3ee06ecf6aac": "Échec de l'exportation",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/docker.json
index b12823cc..e2b278ad 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maskirajte javne IP adrese",
"docker.privacy.mask-network-interfaces": "Maskirajte mrežna sučelja",
"docker.privacy.mask-external-urls": "Maskirajte vanjske URL-ove",
- "docker.actions.capture-diagnostics": "Dijagnostika stranice snimanja"
+ "docker.actions.capture-diagnostics": "Dijagnostika stranice snimanja",
+ "docker.folder.hide": "Sakrij mapu",
+ "docker.folder.restore": "Vrati mapu",
+ "docker.folder.restore-hidden-branch": "Vrati skrivenu granu",
+ "docker.folder.hidden-indicator": "Skriveno",
+ "docker.folder.unnamed": "Mapa",
+ "docker.folder.visibility-failed": "Postavka vidljivosti mape nije mogla biti spremljena.",
+ "docker.visibility.hidden-folders": "Skrivene mape",
+ "docker.visibility.restore-all": "Vratite sve skrivene mape",
+ "docker.visibility.hidden-title": "Mapa skrivena",
+ "docker.visibility.folder-fallback": "Mapa",
+ "docker.visibility.hidden-message": "bio je skriven od pogleda Dockera.",
+ "docker.visibility.undo": "Poništi",
+ "docker.visibility.all-hidden-title": "Svi redovi mape su skriveni.",
+ "docker.visibility.all-hidden-help": "Privremeno ih otkrijte ili vratite svaku skrivenu mapu.",
+ "docker.visibility.show-hidden": "Prikaži skrivene mape",
+ "docker.webui-profiles.open": "Otvorite WebUI profil",
+ "docker.webui-profiles.ready": "$1 ($2 od $3 spreman)",
+ "docker.webui-profiles.manage": "Upravljanje WebUI profilima",
+ "docker.webui-profiles.none-title": "WebUI nije spreman",
+ "docker.webui-profiles.none-body": "Nijedan od odabranih spremnika ne radi s dostupnim WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/editor.json
index 5d87d9cf..a73bf7ee 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Sastavite projekt jednako",
"editor.rules.templates.compose-project-placeholder": "Naziv projekta (primjer: mediji)",
"editor.rules.templates.label-equals": "Docker oznaka je jednaka",
- "editor.rules.templates.label-equals-placeholder": "Oznaka ključ=vrijednost"
+ "editor.rules.templates.label-equals-placeholder": "Oznaka ključ=vrijednost",
+ "editor.webui-profiles.title": "Prilagođeni WebUI profili",
+ "editor.webui-profiles.help": "Stvorite grupe za ponovno korištenje od izravnih članova Dockera ove mape. Trenutne WebUI adrese i stanje rada provjeravaju se samo kada je profil otvoren.",
+ "editor.webui-profiles.add": "Dodaj profil",
+ "editor.webui-profiles.count": "$1 spremljeni profil(i)",
+ "editor.webui-profiles.empty": "Još nema prilagođenih WebUI profila.",
+ "editor.webui-profiles.name": "Naziv profila",
+ "editor.webui-profiles.duplicate": "Duplikat",
+ "editor.webui-profiles.move-up": "Pomakni se gore",
+ "editor.webui-profiles.move-down": "Pomakni se dolje",
+ "editor.webui-profiles.delete": "Izbriši",
+ "editor.webui-profiles.search": "Traži članove mape",
+ "editor.webui-profiles.select-webui": "Odaberite sve s WebUI-jem",
+ "editor.webui-profiles.clear": "Očisti odabir",
+ "editor.webui-profiles.webui-ready": "WebUI konfiguriran",
+ "editor.webui-profiles.webui-runtime": "WebUI provjeren pri pokretanju",
+ "editor.webui-profiles.unavailable": "Nije dostupno u ovoj mapi",
+ "editor.webui-profiles.no-members": "Nema dostupnih odgovarajućih članova mape.",
+ "editor.webui-profiles.error-name": "Svaki WebUI profil treba ime.",
+ "editor.webui-profiles.error-duplicate": "Imena WebUI profila moraju biti jedinstvena unutar ove mape.",
+ "editor.webui-profiles.error-members": "Svaki WebUI profil mora odabrati barem jednog člana mape.",
+ "editor.webui-profiles.limit": "Ova je mapa dosegla sigurnosno ograničenje profila web sučelja."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/legacy-surface.json
index fe33a6dc..40b0c92a 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hr/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nema promjena aplikacije",
"legacy.surface.26f0360c9dddc4d1": "Unaprijed postavka izbrisana",
"legacy.surface.270bca2990500a60": "Odaberite gdje se ova mapa nalazi u hijerarhiji.",
+ "legacy.surface.273c468809bda389": "Postavite boje indikatora i neovisnu boju teksta statusa koju koriste reci mape.",
"legacy.surface.273f838c039ee10f": "FolderView Plus otkrio je da je stranica postavki postala prazna prije dovršetka inicijalizacije.",
"legacy.surface.2753807153755a5e": "Spoji (dodaj + ažuriraj)",
"legacy.surface.2847d4f584d08e0c": "Učitavanje prikaza naredbi Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Početna grana ($1)",
"legacy.surface.32c919087b37ec2d": "Medijski snop",
"legacy.surface.334277e71d658735": "Nakon cilja",
+ "legacy.surface.33541731c2a77842": "Postavite boje indikatora Pokrenuto, Pauzirano i Zaustavljeno neovisno o boji teksta statusa u prikazima Docker, VM i nadzorne ploče.",
"legacy.surface.3388c6d7238cdde6": "Naslov postavki dodatka",
"legacy.surface.33891d013966c572": "Odaberite mapu za skupno dodjeljivanje.",
"legacy.surface.33c5850b76f401e8": "Mapa najviše razine",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Odaberite promjenu stanja i članove koje kontrolira.",
"legacy.surface.613aa3b591bacd9a": "Ukupna pohrana (diskovi s podrškom za datoteke): $1",
"legacy.surface.61c56bacd53c2dfa": "Boje ševrona:",
+ "legacy.surface.61ea108a3718792d": "Stvorite imenovane grupe koje otvaraju samo odabrana web sučelja Docker spremnika.",
"legacy.surface.61f565ba05bd647d": "Podudarni nazivi spremnika ili VM bit će automatski uključeni ovdje.",
"legacy.surface.61fcdf8c82cb687a": "Primijeniti pretpregledane zadatke?",
"legacy.surface.6233afa7701a7fb8": "Uključi prikazano",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Mrdanje",
"legacy.surface.7179ea0035fca783": "Očisti filtre",
"legacy.surface.7188848298a0b500": "Opseg: favoriti",
+ "legacy.surface.71988c4d8e0803ba": "Tekst",
"legacy.surface.719e8555bf1abeba": "Upotrijebite korak Predlošci, uvezite datoteke ili prvo ručno izradite mape.",
"legacy.surface.71b2e8d085cdca1e": "Spremno: sve provjere prošle.",
"legacy.surface.7205aff54d71cb8b": "Prvo kopirajte postavke mape iz druge Docker mape.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Pregled:",
"legacy.surface.75bf7d9afc988cb6": "Mape su razvrstane po nazivu od A do Z. Ugniježđene mape i dalje ostaju unutar svoje nadređene grane.",
"legacy.surface.75d599007fcf49af": "Sve članstvo",
- "legacy.surface.75d7a33335c4f033": "Postavite započete, pauzirane i zaustavljene boje koje koriste reci mape.",
"legacy.surface.7611fe57a952da4b": "Preporučeno. Automatski blokira rizične promjene.",
"legacy.surface.764210a29b968296": "Kategorija predloška",
"legacy.surface.764b74dea7f342ff": "Kontrolira koliko se razina ugniježđenih mapa pojavljuje kao čipovi kada se podređene mape prikazuju u sažetom pregledu.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Tijelo VM tablice",
"legacy.surface.8569ce7917f23eb6": "Izbrisati $1 odabranih pravila?",
"legacy.surface.858e4ba7a29fd38b": "Pauza",
+ "legacy.surface.85b6c17862a72780": "WebUI profili",
"legacy.surface.85c83f4500bb9f2c": "Informacije o vremenu izvođenja VM-a",
"legacy.surface.85e2efe37bad1a44": "Nema prihvatljivih stavki za ovu radnju.",
"legacy.surface.8600edb883562f2c": "Maskirajte LAN IP adrese",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Skenirajte svoje trenutne postavke Dockera i VM mape.",
"legacy.surface.916b423aa46d7493": "Otkriveno je $1 savjetodavno pitanje(a) (dubina/prazna grana), ali nema grešaka veze siroče/ciklusa za automatski popravak.",
"legacy.surface.91f41a1797b442ab": "Ažuriran $1 iz spremljenog predloška.",
- "legacy.surface.920b3e3ce5710d1d": "Postavite prilagođene boje za ikonu statusa ove mape i tekst u prikazima Docker, VM i nadzorne ploče.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Prag upozorenja za pozornost",
"legacy.surface.921b15089caf4ff8": "3. Pregledajte redoslijed pravila",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Kontrolira kako ažuriranja na čekanju utječu na zdravlje ove mape.",
"legacy.surface.926e4eab47bf42ea": "Podudaranja uživo: unesite uzorak regularnog izraza.",
"legacy.surface.92c1cdfdf4cb9cf6": "Omogućeno",
+ "legacy.surface.92cff14b4b7992dd": "Odaberite izravne članove mape koje otvara svaki imenovani profil.",
"legacy.surface.92ec9d61393d7064": "$1 regularni izraz",
"legacy.surface.93aa7d6a1b93167d": "Nisu pronađene prilagođene ikone. Učitajte ikonu da biste započeli.",
"legacy.surface.93b1554a829a9433": "3 razine",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Nespremljene promjene",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "normalno",
- "legacy.surface.a759d24fa1d32402": "Otvorite sva WebUI-ja",
"legacy.surface.a75ee6a28c39f865": "Povucite podređene mape da biste kontrolirali redoslijed prikazan u pregledima i proširenim redcima.",
"legacy.surface.a76ac50faa110d7d": "Prvo najstarije stvorene mape",
"legacy.surface.a76e13b9839270eb": "Vratiti",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Rad na suho",
"legacy.surface.d640c7421da06661": "Nastavi",
"legacy.surface.d66d27cf2aa666c8": "Prikaz izbora",
+ "legacy.surface.d693881b5ac21882": "--fvplus-tekst-statusa-mape",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Unesite postojeću postavku FolderViewa.",
"legacy.surface.d6a226a96bdc60fe": "Blokiraj skočne prozore",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Usporedi",
"legacy.surface.e89b30aa1dc30a6a": "Resursi",
"legacy.surface.e89c3109e1f14c41": "Ciljna mapa",
+ "legacy.surface.e8b4d99f2d672a8e": "Prilagođeni WebUI profili",
"legacy.surface.e8e470943fd0f0b6": "Redoslijed ugniježđenih mapa",
"legacy.surface.e948f182d485afee": "Proširi granu",
"legacy.surface.e94d3ee06ecf6aac": "Izvoz nije uspio",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/docker.json
index dd36f17c..058867ae 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Nyilvános IP-címek maszkolása",
"docker.privacy.mask-network-interfaces": "Maszk hálózati interfészek",
"docker.privacy.mask-external-urls": "Külső URL-ek maszkolása",
- "docker.actions.capture-diagnostics": "Oldaldiagnosztika rögzítése"
+ "docker.actions.capture-diagnostics": "Oldaldiagnosztika rögzítése",
+ "docker.folder.hide": "Mappa elrejtése",
+ "docker.folder.restore": "Mappa visszaállítása",
+ "docker.folder.restore-hidden-branch": "Rejtett ág visszaállítása",
+ "docker.folder.hidden-indicator": "Rejtett",
+ "docker.folder.unnamed": "Mappa",
+ "docker.folder.visibility-failed": "A mappa láthatósági beállítását nem sikerült elmenteni.",
+ "docker.visibility.hidden-folders": "Rejtett mappák",
+ "docker.visibility.restore-all": "Állítsa vissza az összes rejtett mappát",
+ "docker.visibility.hidden-title": "Mappa elrejtve",
+ "docker.visibility.folder-fallback": "Mappa",
+ "docker.visibility.hidden-message": "el volt rejtve a Docker nézet elől.",
+ "docker.visibility.undo": "Visszavonás",
+ "docker.visibility.all-hidden-title": "Minden mappasor el van rejtve.",
+ "docker.visibility.all-hidden-help": "Ideiglenesen fedje fel őket, vagy állítson vissza minden rejtett mappát.",
+ "docker.visibility.show-hidden": "Rejtett mappák megjelenítése",
+ "docker.webui-profiles.open": "Nyissa meg a WebUI profilt",
+ "docker.webui-profiles.ready": "$1 ($2/$3 kész)",
+ "docker.webui-profiles.manage": "WebUI-profilok kezelése",
+ "docker.webui-profiles.none-title": "Nincs kész WebUI",
+ "docker.webui-profiles.none-body": "A kiválasztott tárolók egyike sem fut elérhető WebUI-val."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/editor.json
index 4e04504c..5f21304f 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Projekt összeállítása egyenlő",
"editor.rules.templates.compose-project-placeholder": "Projekt neve (példa: média)",
"editor.rules.templates.label-equals": "Docker címke egyenlő",
- "editor.rules.templates.label-equals-placeholder": "Címke kulcs=érték"
+ "editor.rules.templates.label-equals-placeholder": "Címke kulcs=érték",
+ "editor.webui-profiles.title": "Egyéni WebUI profilok",
+ "editor.webui-profiles.help": "Hozzon létre újrafelhasználható indítócsoportokat a mappa közvetlen Docker-tagjaiból. Az aktuális WebUI-címek és a futó állapot ellenőrzése csak profil megnyitásakor történik.",
+ "editor.webui-profiles.add": "Profil hozzáadása",
+ "editor.webui-profiles.count": "$1 mentett profil(ok)",
+ "editor.webui-profiles.empty": "Még nincsenek egyéni WebUI-profilok.",
+ "editor.webui-profiles.name": "Profil neve",
+ "editor.webui-profiles.duplicate": "Megkettőzés",
+ "editor.webui-profiles.move-up": "Lépj felfelé",
+ "editor.webui-profiles.move-down": "Mozgás lefelé",
+ "editor.webui-profiles.delete": "Törölje",
+ "editor.webui-profiles.search": "Mappatagok keresése",
+ "editor.webui-profiles.select-webui": "Válassza ki az összeset a WebUI-val",
+ "editor.webui-profiles.clear": "Kiválasztás törlése",
+ "editor.webui-profiles.webui-ready": "WebUI konfigurálva",
+ "editor.webui-profiles.webui-runtime": "A WebUI indításkor ellenőrizve",
+ "editor.webui-profiles.unavailable": "Nem érhető el ebben a mappában",
+ "editor.webui-profiles.no-members": "Nincsenek megfelelő mappatagok.",
+ "editor.webui-profiles.error-name": "Minden WebUI-profilnak szüksége van egy névre.",
+ "editor.webui-profiles.error-duplicate": "A WebUI-profilneveknek egyedinek kell lenniük ebben a mappában.",
+ "editor.webui-profiles.error-members": "Minden WebUI-profilnak ki kell választania legalább egy mappatagot.",
+ "editor.webui-profiles.limit": "Ez a mappa elérte a WebUI-profil biztonsági korlátját."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/legacy-surface.json
index 84777a8c..bab46216 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/hu/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nincsenek alkalmazásmódosítások",
"legacy.surface.26f0360c9dddc4d1": "Előbeállítás törölve",
"legacy.surface.270bca2990500a60": "Válassza ki a mappa helyét a hierarchiában.",
+ "legacy.surface.273c468809bda389": "Állítsa be a jelzőszíneket és a mappasorok által használt független állapotszöveg színét.",
"legacy.surface.273f838c039ee10f": "A FolderView Plus észlelte, hogy a Beállítások oldal üressé vált az inicializálás befejezése előtt.",
"legacy.surface.2753807153755a5e": "Egyesítés (hozzáadás + frissítés)",
"legacy.surface.2847d4f584d08e0c": "Docker parancsnézet betöltése...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Elágazás indítása ($1)",
"legacy.surface.32c919087b37ec2d": "Media Stack",
"legacy.surface.334277e71d658735": "Cél után",
+ "legacy.surface.33541731c2a77842": "Állítsa be az Indított, Szüneteltetve és Leállított jelzőszíneket az állapotszöveg színétől függetlenül a Docker, a virtuális gépek és az irányítópult nézetekben.",
"legacy.surface.3388c6d7238cdde6": "Plugin beállítások címe",
"legacy.surface.33891d013966c572": "Válasszon mappát a tömeges hozzárendeléshez.",
"legacy.surface.33c5850b76f401e8": "Legfelső szintű mappa",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Válassza ki az állapotváltozást és azt, hogy mely tagokat szabályozza.",
"legacy.surface.613aa3b591bacd9a": "Teljes tárhely (fájllal támogatott lemezek): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron színei:",
+ "legacy.surface.61ea108a3718792d": "Hozzon létre elnevezett csoportokat, amelyek csak kiválasztott Docker-tároló webUI-kat nyitnak meg.",
"legacy.surface.61f565ba05bd647d": "A megfelelő tároló- vagy virtuálisgép-nevek automatikusan megjelennek itt.",
"legacy.surface.61fcdf8c82cb687a": "Alkalmazza az előnézeti feladatokat?",
"legacy.surface.6233afa7701a7fb8": "Tartalmazza látható",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Mozog",
"legacy.surface.7179ea0035fca783": "Tiszta szűrők",
"legacy.surface.7188848298a0b500": "Terjedelem: kedvencek",
+ "legacy.surface.71988c4d8e0803ba": "Szöveg",
"legacy.surface.719e8555bf1abeba": "Először használja a Sablonok lépést, importáljon fájlokat, vagy hozzon létre mappákat manuálisan.",
"legacy.surface.71b2e8d085cdca1e": "Kész: minden ellenőrzés sikeres.",
"legacy.surface.7205aff54d71cb8b": "Először másolja át a mappabeállításokat egy másik Docker mappából.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Előnézet:",
"legacy.surface.75bf7d9afc988cb6": "A mappák név szerint vannak rendezve A-tól Z-ig. A beágyazott mappák továbbra is a szülőágukon belül maradnak.",
"legacy.surface.75d599007fcf49af": "Minden tagság",
- "legacy.surface.75d7a33335c4f033": "Állítsa be a mappasorok által használt elindított, szüneteltetett és leállított színeket.",
"legacy.surface.7611fe57a952da4b": "Ajánlott. Automatikusan blokkolja a kockázatos változtatásokat.",
"legacy.surface.764210a29b968296": "Sablon kategória",
"legacy.surface.764b74dea7f342ff": "Azt szabályozza, hogy hány beágyazott mappaszint jelenjen meg chipként, amikor a gyermekmappák megjelennek az összecsukott előnézetben.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM táblázat törzse",
"legacy.surface.8569ce7917f23eb6": "Töröl $1 kiválasztott szabályo(ka)t?",
"legacy.surface.858e4ba7a29fd38b": "Szünet",
+ "legacy.surface.85b6c17862a72780": "WebUI profilok",
"legacy.surface.85c83f4500bb9f2c": "VM futásidejű információ",
"legacy.surface.85e2efe37bad1a44": "Nincsenek jogosult elemek ehhez a művelethez.",
"legacy.surface.8600edb883562f2c": "Maszkolja a LAN IP-ket",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Vizsgálja át a jelenlegi Docker- és VM-mappabeállításokat.",
"legacy.surface.916b423aa46d7493": "A rendszer $1 tanácsadó problémát észlelt (mélység/üres ág), de nincs árva/ciklushiba az automatikus javításhoz.",
"legacy.surface.91f41a1797b442ab": "$1 frissítve a mentett sablonból.",
- "legacy.surface.920b3e3ce5710d1d": "Állítson be egyéni színeket a mappa állapotikonjához és szövegéhez a Docker, a virtuális gépek és az irányítópult nézeteiben.",
"legacy.surface.920e413c7d411b61": "Állapot",
"legacy.surface.9213c7f4046c085d": "Figyelmeztetési küszöb",
"legacy.surface.921b15089caf4ff8": "3. Tekintse át a szabályok sorrendjét",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Azt szabályozza, hogy a függőben lévő frissítések hogyan befolyásolják a mappa állapotát.",
"legacy.surface.926e4eab47bf42ea": "Élő mérkőzések: írjon be egy regex mintát.",
"legacy.surface.92c1cdfdf4cb9cf6": "Engedélyezve",
+ "legacy.surface.92cff14b4b7992dd": "Válassza ki az egyes elnevezett profilok által megnyitott közvetlen mappatagokat.",
"legacy.surface.92ec9d61393d7064": "$1 reguláris kifejezés",
"legacy.surface.93aa7d6a1b93167d": "Nem található egyéni ikon. A kezdéshez töltsön fel egy ikont.",
"legacy.surface.93b1554a829a9433": "3 szint",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Nem mentett módosítások",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normális",
- "legacy.surface.a759d24fa1d32402": "Nyissa meg az összes WebUI-t",
"legacy.surface.a75ee6a28c39f865": "A gyermekmappák húzásával szabályozhatja az előnézetekben és a kibontott sorokban megjelenő sorrendet.",
"legacy.surface.a76ac50faa110d7d": "A legrégebben létrehozott mappák először",
"legacy.surface.a76e13b9839270eb": "Visszaállítás",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Száraz futás",
"legacy.surface.d640c7421da06661": "Folytatás",
"legacy.surface.d66d27cf2aa666c8": "Megjelenítési lehetőségek",
+ "legacy.surface.d693881b5ac21882": "--fvplus-mappa-állapot-szöveg",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Hozzon létre egy meglévő FolderView beállítást.",
"legacy.surface.d6a226a96bdc60fe": "Előugró ablakok blokkolása",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Hasonlítsa össze",
"legacy.surface.e89b30aa1dc30a6a": "Erőforrások",
"legacy.surface.e89c3109e1f14c41": "Célmappa",
+ "legacy.surface.e8b4d99f2d672a8e": "Egyéni WebUI-profilok",
"legacy.surface.e8e470943fd0f0b6": "Beágyazott mappa sorrend",
"legacy.surface.e948f182d485afee": "Elágazás bővítése",
"legacy.surface.e94d3ee06ecf6aac": "Nem sikerült exportálni",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/docker.json
index 06ef7d25..42fbd01d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maschera IPs pubblici",
"docker.privacy.mask-network-interfaces": "Maschera interfacce di rete",
"docker.privacy.mask-external-urls": "Maschera URL esterni",
- "docker.actions.capture-diagnostics": "Cattura la diagnostica della pagina"
+ "docker.actions.capture-diagnostics": "Cattura la diagnostica della pagina",
+ "docker.folder.hide": "Nascondi cartella",
+ "docker.folder.restore": "Ripristina cartella",
+ "docker.folder.restore-hidden-branch": "Ripristina il ramo nascosto",
+ "docker.folder.hidden-indicator": "Nascosto",
+ "docker.folder.unnamed": "Cartella",
+ "docker.folder.visibility-failed": "Impossibile salvare la preferenza di visibilità della cartella.",
+ "docker.visibility.hidden-folders": "Cartelle nascoste",
+ "docker.visibility.restore-all": "Ripristina tutte le cartelle nascoste",
+ "docker.visibility.hidden-title": "Cartella nascosta",
+ "docker.visibility.folder-fallback": "Cartella",
+ "docker.visibility.hidden-message": "era nascosto dalla vista Docker.",
+ "docker.visibility.undo": "Annulla",
+ "docker.visibility.all-hidden-title": "Tutte le righe delle cartelle sono nascoste.",
+ "docker.visibility.all-hidden-help": "Riveli temporaneamente o ripristina ogni cartella nascosta.",
+ "docker.visibility.show-hidden": "Mostra cartelle nascoste",
+ "docker.webui-profiles.open": "Apri il profilo WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 di $3 pronto)",
+ "docker.webui-profiles.manage": "Gestire i profili WebUI",
+ "docker.webui-profiles.none-title": "Nessuna WebUI pronta",
+ "docker.webui-profiles.none-body": "Nessuno dei contenitori selezionati è in esecuzione con una WebUI disponibile."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/editor.json
index d61182a4..75c13bd5 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Componi progetto uguale",
"editor.rules.templates.compose-project-placeholder": "Nome del progetto (esempio: media)",
"editor.rules.templates.label-equals": "L'etichetta Docker è uguale a",
- "editor.rules.templates.label-equals-placeholder": "Etichetta chiave=valore"
+ "editor.rules.templates.label-equals-placeholder": "Etichetta chiave=valore",
+ "editor.webui-profiles.title": "Profili WebUI personalizzati",
+ "editor.webui-profiles.help": "Crea gruppi di avvio riutilizzabili dai membri Docker diretti di questa cartella. Gli indirizzi WebUI correnti e lo stato di esecuzione vengono controllati solo quando viene aperto un profilo.",
+ "editor.webui-profiles.add": "Aggiungi profilo",
+ "editor.webui-profiles.count": "$1 profilo/i salvato/i",
+ "editor.webui-profiles.empty": "Nessun profilo WebUI personalizzato ancora.",
+ "editor.webui-profiles.name": "Nome del profilo",
+ "editor.webui-profiles.duplicate": "Duplicato",
+ "editor.webui-profiles.move-up": "Vai su",
+ "editor.webui-profiles.move-down": "Spostati giù",
+ "editor.webui-profiles.delete": "Elimina",
+ "editor.webui-profiles.search": "Cerca membri della cartella",
+ "editor.webui-profiles.select-webui": "Seleziona tutto con WebUI",
+ "editor.webui-profiles.clear": "Cancella selezione",
+ "editor.webui-profiles.webui-ready": "WebUI configurato",
+ "editor.webui-profiles.webui-runtime": "WebUI controllato all'avvio",
+ "editor.webui-profiles.unavailable": "Non disponibile in questa cartella",
+ "editor.webui-profiles.no-members": "Non sono disponibili membri della cartella corrispondenti.",
+ "editor.webui-profiles.error-name": "Ogni profilo WebUI necessita di un nome.",
+ "editor.webui-profiles.error-duplicate": "I nomi dei profili WebUI devono essere univoci all'interno di questa cartella.",
+ "editor.webui-profiles.error-members": "Ogni profilo WebUI deve selezionare almeno un membro della cartella.",
+ "editor.webui-profiles.limit": "Questa cartella ha raggiunto il limite di sicurezza del profilo WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/legacy-surface.json
index c8b9a478..fc3cae0f 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/it/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nessuna modifica all'app",
"legacy.surface.26f0360c9dddc4d1": "Preimpostazione eliminata",
"legacy.surface.270bca2990500a60": "Scegli dove risiede questa cartella nella gerarchia.",
+ "legacy.surface.273c468809bda389": "Imposta i colori degli indicatori e il colore del testo di stato indipendente utilizzato dalle righe della cartella.",
"legacy.surface.273f838c039ee10f": "FolderView Plus ha rilevato che la pagina Impostazioni è diventata vuota prima del completamento dell'inizializzazione.",
"legacy.surface.2753807153755a5e": "Unisci (aggiungi + aggiorna)",
"legacy.surface.2847d4f584d08e0c": "Caricamento della vista dei comandi Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Avvia ramo ($1)",
"legacy.surface.32c919087b37ec2d": "Pila multimediale",
"legacy.surface.334277e71d658735": "Dopo l'obiettivo",
+ "legacy.surface.33541731c2a77842": "Imposta i colori degli indicatori Avviato, In pausa e Interrotto indipendentemente dal colore del testo dello stato nelle visualizzazioni Docker, VM e Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Titolo delle impostazioni del plugin",
"legacy.surface.33891d013966c572": "Seleziona una cartella per l'assegnazione in blocco.",
"legacy.surface.33c5850b76f401e8": "Cartella di livello superiore",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Scegli il cambiamento di stato e quali membri controlla.",
"legacy.surface.613aa3b591bacd9a": "Totale spazio di archiviazione (dischi con backup su file): $1",
"legacy.surface.61c56bacd53c2dfa": "Colori Chevron:",
+ "legacy.surface.61ea108a3718792d": "Crea gruppi denominati che aprono solo le WebUI del contenitore Docker selezionate.",
"legacy.surface.61f565ba05bd647d": "I nomi dei contenitori o delle VM corrispondenti verranno inclusi automaticamente qui.",
"legacy.surface.61fcdf8c82cb687a": "Applicare i compiti previsti in anteprima?",
"legacy.surface.6233afa7701a7fb8": "Includi mostrato",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Muovere",
"legacy.surface.7179ea0035fca783": "Cancella filtri",
"legacy.surface.7188848298a0b500": "Ambito: preferiti",
+ "legacy.surface.71988c4d8e0803ba": "Testo",
"legacy.surface.719e8555bf1abeba": "Utilizzare prima il passaggio Modelli, importare file o creare cartelle manualmente.",
"legacy.surface.71b2e8d085cdca1e": "Pronto: tutti i controlli superati.",
"legacy.surface.7205aff54d71cb8b": "Copia prima le impostazioni della cartella da un'altra cartella Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Anteprima:",
"legacy.surface.75bf7d9afc988cb6": "Le cartelle sono ordinate per nome dalla A alla Z. Le cartelle nidificate rimangono comunque all'interno del ramo principale.",
"legacy.surface.75d599007fcf49af": "Tutti gli abbonamenti",
- "legacy.surface.75d7a33335c4f033": "Imposta i colori avviati, in pausa e interrotti utilizzati dalle righe della cartella.",
"legacy.surface.7611fe57a952da4b": "Consigliato. Blocca automaticamente le modifiche rischiose.",
"legacy.surface.764210a29b968296": "Categoria modello",
"legacy.surface.764b74dea7f342ff": "Controlla il numero di livelli di cartelle nidificate visualizzati come chip quando le cartelle secondarie vengono visualizzate nell'anteprima compressa.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Corpo della tabella VM",
"legacy.surface.8569ce7917f23eb6": "Eliminare $1 regola/e selezionata/e?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "Profili dell'interfaccia utente Web",
"legacy.surface.85c83f4500bb9f2c": "Informazioni sull'esecuzione della VM",
"legacy.surface.85e2efe37bad1a44": "Nessun articolo idoneo per questa azione.",
"legacy.surface.8600edb883562f2c": "Maschera gli IP LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Esegui la scansione della configurazione attuale della cartella Docker e VM.",
"legacy.surface.916b423aa46d7493": "Rilevati problemi di avviso $1 (ramo vuoto/profondità), ma nessun errore di collegamento orfano/ciclo per la riparazione automatica.",
"legacy.surface.91f41a1797b442ab": "Aggiornato $1 dal modello salvato.",
- "legacy.surface.920b3e3ce5710d1d": "Imposta colori personalizzati per l'icona di stato e il testo di questa cartella nelle visualizzazioni Docker, VM e Dashboard.",
"legacy.surface.920e413c7d411b61": "Stato",
"legacy.surface.9213c7f4046c085d": "Soglia di avviso per l'attenzione",
"legacy.surface.921b15089caf4ff8": "3. Rivedi l'ordine delle regole",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controlla il modo in cui gli aggiornamenti in sospeso influiscono sull'integrità di questa cartella.",
"legacy.surface.926e4eab47bf42ea": "Partite dal vivo: inserisci un modello regex.",
"legacy.surface.92c1cdfdf4cb9cf6": "Abilitato",
+ "legacy.surface.92cff14b4b7992dd": "Selezionare i membri diretti della cartella aperti da ciascun profilo denominato.",
"legacy.surface.92ec9d61393d7064": "$1 espressione regolare",
"legacy.surface.93aa7d6a1b93167d": "Nessuna icona personalizzata trovata. Carica un'icona per iniziare.",
"legacy.surface.93b1554a829a9433": "3 livelli",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Modifiche non salvate",
"legacy.surface.a71d508e50432f40": "Laboratorio domestico",
"legacy.surface.a7248eeb45eb1ce2": "Normale",
- "legacy.surface.a759d24fa1d32402": "Apri tutte le WebUI",
"legacy.surface.a75ee6a28c39f865": "Trascina le cartelle secondarie per controllare l'ordine mostrato nelle anteprime e nelle righe espanse.",
"legacy.surface.a76ac50faa110d7d": "Prima le cartelle create più vecchie",
"legacy.surface.a76e13b9839270eb": "Ripristina",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Prova a secco",
"legacy.surface.d640c7421da06661": "Riprendi",
"legacy.surface.d66d27cf2aa666c8": "Visualizzare le scelte",
+ "legacy.surface.d693881b5ac21882": "--fvplus-testo-stato-cartella",
"legacy.surface.d696a35bdd1883da": "Profilo",
"legacy.surface.d69b03bf0620f859": "Inserisci una configurazione di Visualizzazione cartelle esistente.",
"legacy.surface.d6a226a96bdc60fe": "Blocca i popup",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Confronta",
"legacy.surface.e89b30aa1dc30a6a": "Risorse",
"legacy.surface.e89c3109e1f14c41": "Cartella di destinazione",
+ "legacy.surface.e8b4d99f2d672a8e": "Profili WebUI personalizzati",
"legacy.surface.e8e470943fd0f0b6": "Ordine delle cartelle nidificate",
"legacy.surface.e948f182d485afee": "Espandi ramo",
"legacy.surface.e94d3ee06ecf6aac": "Esportazione non riuscita",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/docker.json
index 1c9b5b56..7dd2a1c2 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "公開IPsを隠す",
"docker.privacy.mask-network-interfaces": "ネットワークインターフェイスを隠す",
"docker.privacy.mask-external-urls": "外部URLをマスクする",
- "docker.actions.capture-diagnostics": "ページ診断のキャプチャ"
+ "docker.actions.capture-diagnostics": "ページ診断のキャプチャ",
+ "docker.folder.hide": "フォルダを隠す",
+ "docker.folder.restore": "フォルダを復元する",
+ "docker.folder.restore-hidden-branch": "隠されたブランチを復元する",
+ "docker.folder.hidden-indicator": "隠された",
+ "docker.folder.unnamed": "フォルダー",
+ "docker.folder.visibility-failed": "フォルダーの表示設定を保存できませんでした。",
+ "docker.visibility.hidden-folders": "隠しフォルダー",
+ "docker.visibility.restore-all": "すべての隠しフォルダーを復元する",
+ "docker.visibility.hidden-title": "隠しフォルダー",
+ "docker.visibility.folder-fallback": "フォルダー",
+ "docker.visibility.hidden-message": "Docker ビューから隠蔽されました。",
+ "docker.visibility.undo": "元に戻す",
+ "docker.visibility.all-hidden-title": "すべてのフォルダー行が非表示になります。",
+ "docker.visibility.all-hidden-help": "一時的に表示するか、すべての隠しフォルダーを復元します。",
+ "docker.visibility.show-hidden": "隠しフォルダーを表示する",
+ "docker.webui-profiles.open": "WebUI プロファイルを開く",
+ "docker.webui-profiles.ready": "$1($3件中$2件準備完了)",
+ "docker.webui-profiles.manage": "WebUIプロファイルの管理",
+ "docker.webui-profiles.none-title": "WebUI が準備されていません",
+ "docker.webui-profiles.none-body": "選択したコンテナはどれも、利用可能な WebUI で実行されていません。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/editor.json
index d41c0ff2..87057373 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "プロジェクトを同等に構成する",
"editor.rules.templates.compose-project-placeholder": "プロジェクト名(例:メディア)",
"editor.rules.templates.label-equals": "Docker ラベルが等しい",
- "editor.rules.templates.label-equals-placeholder": "ラベルキー=値"
+ "editor.rules.templates.label-equals-placeholder": "ラベルキー=値",
+ "editor.webui-profiles.title": "カスタム WebUI プロファイル",
+ "editor.webui-profiles.help": "このフォルダーの直接の Docker メンバーから再利用可能な起動グループを作成します。現在の WebUI アドレスと実行状態は、プロファイルを開いたときにのみチェックされます。",
+ "editor.webui-profiles.add": "プロフィールを追加",
+ "editor.webui-profiles.count": "$1 個の保存済みプロファイル",
+ "editor.webui-profiles.empty": "カスタム WebUI プロファイルはまだありません。",
+ "editor.webui-profiles.name": "プロファイル名",
+ "editor.webui-profiles.duplicate": "重複",
+ "editor.webui-profiles.move-up": "上に移動",
+ "editor.webui-profiles.move-down": "下に移動",
+ "editor.webui-profiles.delete": "削除",
+ "editor.webui-profiles.search": "フォルダーメンバーを検索する",
+ "editor.webui-profiles.select-webui": "WebUIですべて選択",
+ "editor.webui-profiles.clear": "選択をクリア",
+ "editor.webui-profiles.webui-ready": "WebUIの設定済み",
+ "editor.webui-profiles.webui-runtime": "起動時にWebUIをチェック",
+ "editor.webui-profiles.unavailable": "このフォルダでは使用できません",
+ "editor.webui-profiles.no-members": "一致するフォルダー メンバーがありません。",
+ "editor.webui-profiles.error-name": "すべての WebUI プロファイルには名前が必要です。",
+ "editor.webui-profiles.error-duplicate": "WebUI プロファイル名は、このフォルダー内で一意である必要があります。",
+ "editor.webui-profiles.error-members": "すべての WebUI プロファイルは、少なくとも 1 つのフォルダー メンバーを選択する必要があります。",
+ "editor.webui-profiles.limit": "このフォルダーは WebUI プロファイルの安全制限に達しています。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/legacy-surface.json
index 2aa74ab7..6752fa1b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ja/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "アプリの変更はありません",
"legacy.surface.26f0360c9dddc4d1": "プリセットが削除されました",
"legacy.surface.270bca2990500a60": "このフォルダーが階層内のどこに存在するかを選択します。",
+ "legacy.surface.273c468809bda389": "フォルダー行で使用されるインジケーターの色と独立したステータス テキストの色を設定します。",
"legacy.surface.273f838c039ee10f": "FolderView Plus は、初期化が完了する前に [設定] ページが空白になったことを検出しました。",
"legacy.surface.2753807153755a5e": "マージ (追加 + 更新)",
"legacy.surface.2847d4f584d08e0c": "Docker コマンド ビューを読み込み中...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "開始ブランチ ($1)",
"legacy.surface.32c919087b37ec2d": "メディアスタック",
"legacy.surface.334277e71d658735": "ターゲット後",
+ "legacy.surface.33541731c2a77842": "Docker、VM、およびダッシュボード ビュー全体のステータス テキストの色とは別に、開始、一時停止、および停止のインジケーターの色を設定します。",
"legacy.surface.3388c6d7238cdde6": "プラグイン設定のタイトル",
"legacy.surface.33891d013966c572": "一括割り当てするフォルダーを選択します。",
"legacy.surface.33c5850b76f401e8": "最上位のフォルダー",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "状態の変更と、それが制御するメンバーを選択します。",
"legacy.surface.613aa3b591bacd9a": "ストレージの合計 (ファイルバッキング ディスク): $1",
"legacy.surface.61c56bacd53c2dfa": "シェブロンの色:",
+ "legacy.surface.61ea108a3718792d": "選択した Docker コンテナー WebUI のみを開く名前付きグループを作成します。",
"legacy.surface.61f565ba05bd647d": "一致するコンテナーまたは VM 名がここに自動的に含まれます。",
"legacy.surface.61fcdf8c82cb687a": "プレビューした割り当てを適用しますか?",
"legacy.surface.6233afa7701a7fb8": "表示を含む",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "小刻みに動く",
"legacy.surface.7179ea0035fca783": "フィルターをクリアする",
"legacy.surface.7188848298a0b500": "範囲: お気に入り",
+ "legacy.surface.71988c4d8e0803ba": "テキスト",
"legacy.surface.719e8555bf1abeba": "最初に「テンプレート」ステップを使用してファイルをインポートするか、フォルダーを手動で作成します。",
"legacy.surface.71b2e8d085cdca1e": "準備完了: すべてのチェックに合格しました。",
"legacy.surface.7205aff54d71cb8b": "まず、別の Docker フォルダーからフォルダー設定をコピーします。",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "プレビュー:",
"legacy.surface.75bf7d9afc988cb6": "フォルダーは A から Z までの名前で並べ替えられます。ネストされたフォルダーは親ブランチ内に残ります。",
"legacy.surface.75d599007fcf49af": "すべての会員",
- "legacy.surface.75d7a33335c4f033": "フォルダー行で使用される開始色、一時停止色、停止色を設定します。",
"legacy.surface.7611fe57a952da4b": "おすすめです。危険な変更を自動的にブロックします。",
"legacy.surface.764210a29b968296": "テンプレートカテゴリ",
"legacy.surface.764b74dea7f342ff": "子フォルダーが折りたたまれたプレビューに表示されるときに、ネストされたフォルダー レベルの数がチップとして表示されるかを制御します。",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VMテーブル本体",
"legacy.surface.8569ce7917f23eb6": "選択した $1 個のルールを削除しますか?",
"legacy.surface.858e4ba7a29fd38b": "一時停止",
+ "legacy.surface.85b6c17862a72780": "WebUI プロファイル",
"legacy.surface.85c83f4500bb9f2c": "VM ランタイム情報",
"legacy.surface.85e2efe37bad1a44": "このアクションの対象となるアイテムはありません。",
"legacy.surface.8600edb883562f2c": "LAN IP のマスク",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "現在の Docker と VM フォルダーの設定をスキャンします。",
"legacy.surface.916b423aa46d7493": "$1 アドバイザリの問題 (深さ/空のブランチ) が検出されましたが、自動修復する孤立/サイクル リンク エラーはありません。",
"legacy.surface.91f41a1797b442ab": "保存されたテンプレートから $1 を更新しました。",
- "legacy.surface.920b3e3ce5710d1d": "Docker、VM、およびダッシュボード ビュー全体で、このフォルダーのステータス アイコンとテキストのカスタム カラーを設定します。",
"legacy.surface.920e413c7d411b61": "ステータス",
"legacy.surface.9213c7f4046c085d": "注意を喚起するための警告しきい値",
"legacy.surface.921b15089caf4ff8": "3. ルールの順序を確認する",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "保留中の更新がこのフォルダーの状態にどのような影響を与えるかを制御します。",
"legacy.surface.926e4eab47bf42ea": "ライブマッチ: 正規表現パターンを入力します。",
"legacy.surface.92c1cdfdf4cb9cf6": "有効",
+ "legacy.surface.92cff14b4b7992dd": "各名前付きプロファイルによって開かれた直接のフォルダー メンバーを選択します。",
"legacy.surface.92ec9d61393d7064": "$1 正規表現",
"legacy.surface.93aa7d6a1b93167d": "カスタム アイコンが見つかりません。開始するには、アイコンをアップロードしてください。",
"legacy.surface.93b1554a829a9433": "3レベル",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "未保存の変更",
"legacy.surface.a71d508e50432f40": "ホームラボ",
"legacy.surface.a7248eeb45eb1ce2": "ノーマル",
- "legacy.surface.a759d24fa1d32402": "すべての WebUI を開く",
"legacy.surface.a75ee6a28c39f865": "子フォルダーをドラッグして、プレビューおよび展開された行に表示される順序を制御します。",
"legacy.surface.a76ac50faa110d7d": "作成された最も古いフォルダーが最初に表示されます",
"legacy.surface.a76e13b9839270eb": "復元",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "ドライラン",
"legacy.surface.d640c7421da06661": "再開",
"legacy.surface.d66d27cf2aa666c8": "表示の選択肢",
+ "legacy.surface.d693881b5ac21882": "--fvplus-フォルダーのステータステキスト",
"legacy.surface.d696a35bdd1883da": "プロフィール",
"legacy.surface.d69b03bf0620f859": "既存の FolderView セットアップを取り込みます。",
"legacy.surface.d6a226a96bdc60fe": "ポップアップをブロックする",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "比較する",
"legacy.surface.e89b30aa1dc30a6a": "リソース",
"legacy.surface.e89c3109e1f14c41": "対象フォルダ",
+ "legacy.surface.e8b4d99f2d672a8e": "カスタム WebUI プロファイル",
"legacy.surface.e8e470943fd0f0b6": "ネストされたフォルダーの順序",
"legacy.surface.e948f182d485afee": "ブランチを展開する",
"legacy.surface.e94d3ee06ecf6aac": "エクスポートに失敗しました",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/docker.json
index d190d40a..40452d3f 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "공개 IPs 숨기기",
"docker.privacy.mask-network-interfaces": "네트워크 인터페이스 숨기기",
"docker.privacy.mask-external-urls": "외부 URL 가리기",
- "docker.actions.capture-diagnostics": "캡처 페이지 진단"
+ "docker.actions.capture-diagnostics": "캡처 페이지 진단",
+ "docker.folder.hide": "폴더 숨기기",
+ "docker.folder.restore": "폴더 복원",
+ "docker.folder.restore-hidden-branch": "숨겨진 지점 복원",
+ "docker.folder.hidden-indicator": "숨겨진",
+ "docker.folder.unnamed": "폴더",
+ "docker.folder.visibility-failed": "폴더 공개 상태 기본 설정을 저장할 수 없습니다.",
+ "docker.visibility.hidden-folders": "숨겨진 폴더",
+ "docker.visibility.restore-all": "모든 숨겨진 폴더 복원",
+ "docker.visibility.hidden-title": "폴더 숨김",
+ "docker.visibility.folder-fallback": "폴더",
+ "docker.visibility.hidden-message": "Docker 보기에서 숨겨졌습니다.",
+ "docker.visibility.undo": "실행 취소",
+ "docker.visibility.all-hidden-title": "모든 폴더 행이 숨겨집니다.",
+ "docker.visibility.all-hidden-help": "임시로 공개하거나 숨겨진 폴더를 모두 복원하세요.",
+ "docker.visibility.show-hidden": "숨겨진 폴더 표시",
+ "docker.webui-profiles.open": "WebUI 프로필 열기",
+ "docker.webui-profiles.ready": "$1($3 중 $2 준비됨)",
+ "docker.webui-profiles.manage": "WebUI 프로필 관리",
+ "docker.webui-profiles.none-title": "준비된 WebUI가 없습니다.",
+ "docker.webui-profiles.none-body": "선택한 컨테이너 중 사용 가능한 WebUI로 실행 중인 컨테이너가 없습니다."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/editor.json
index 227ccd90..5727ced7 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "프로젝트 작성은 다음과 같습니다.",
"editor.rules.templates.compose-project-placeholder": "프로젝트 이름(예: 미디어)",
"editor.rules.templates.label-equals": "Docker 라벨은 다음과 같습니다.",
- "editor.rules.templates.label-equals-placeholder": "라벨 키=값"
+ "editor.rules.templates.label-equals-placeholder": "라벨 키=값",
+ "editor.webui-profiles.title": "사용자 정의 WebUI 프로필",
+ "editor.webui-profiles.help": "이 폴더의 직접 Docker 구성원에서 재사용 가능한 시작 그룹을 만듭니다. 현재 WebUI 주소와 실행 상태는 프로필이 열려 있을 때만 확인됩니다.",
+ "editor.webui-profiles.add": "프로필 추가",
+ "editor.webui-profiles.count": "$1 저장된 프로필",
+ "editor.webui-profiles.empty": "아직 맞춤 WebUI 프로필이 없습니다.",
+ "editor.webui-profiles.name": "프로필 이름",
+ "editor.webui-profiles.duplicate": "중복",
+ "editor.webui-profiles.move-up": "위로 이동",
+ "editor.webui-profiles.move-down": "아래로 이동",
+ "editor.webui-profiles.delete": "삭제",
+ "editor.webui-profiles.search": "폴더 구성원 검색",
+ "editor.webui-profiles.select-webui": "WebUI로 모두 선택",
+ "editor.webui-profiles.clear": "선택 취소",
+ "editor.webui-profiles.webui-ready": "WebUI 구성됨",
+ "editor.webui-profiles.webui-runtime": "실행 시 WebUI 확인",
+ "editor.webui-profiles.unavailable": "이 폴더에서는 사용할 수 없습니다.",
+ "editor.webui-profiles.no-members": "일치하는 폴더 구성원이 없습니다.",
+ "editor.webui-profiles.error-name": "모든 WebUI 프로필에는 이름이 필요합니다.",
+ "editor.webui-profiles.error-duplicate": "WebUI 프로필 이름은 이 폴더 내에서 고유해야 합니다.",
+ "editor.webui-profiles.error-members": "모든 WebUI 프로필은 하나 이상의 폴더 구성원을 선택해야 합니다.",
+ "editor.webui-profiles.limit": "이 폴더는 WebUI 프로필 안전 제한에 도달했습니다."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/legacy-surface.json
index d26afbf7..5f2d00f1 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ko/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "앱 변경 없음",
"legacy.surface.26f0360c9dddc4d1": "사전 설정이 삭제되었습니다.",
"legacy.surface.270bca2990500a60": "이 폴더가 계층 구조에서 어디에 위치하는지 선택하세요.",
+ "legacy.surface.273c468809bda389": "폴더 행에 사용되는 표시기 색상과 독립적인 상태 텍스트 색상을 설정합니다.",
"legacy.surface.273f838c039ee10f": "FolderView Plus는 초기화가 완료되기 전에 설정 페이지가 비어 있는 것을 감지했습니다.",
"legacy.surface.2753807153755a5e": "병합(추가 + 업데이트)",
"legacy.surface.2847d4f584d08e0c": "Docker 명령 보기 로드 중...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "지점 시작 ($1)",
"legacy.surface.32c919087b37ec2d": "미디어 스택",
"legacy.surface.334277e71d658735": "목표 후",
+ "legacy.surface.33541731c2a77842": "Docker, VM 및 대시보드 보기 전체에서 상태 텍스트 색상과 별도로 시작됨, 일시 중지됨 및 중지됨 표시기 색상을 설정합니다.",
"legacy.surface.3388c6d7238cdde6": "플러그인 설정 제목",
"legacy.surface.33891d013966c572": "일괄 할당할 폴더를 선택합니다.",
"legacy.surface.33c5850b76f401e8": "최상위 폴더",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "상태 변경과 해당 변경이 제어하는 멤버를 선택합니다.",
"legacy.surface.613aa3b591bacd9a": "총 스토리지(파일 지원 디스크): $1",
"legacy.surface.61c56bacd53c2dfa": "쉐브론 색상:",
+ "legacy.surface.61ea108a3718792d": "선택한 Docker 컨테이너 WebUI만 여는 명명된 그룹을 만듭니다.",
"legacy.surface.61f565ba05bd647d": "일치하는 컨테이너 또는 VM 이름이 여기에 자동으로 포함됩니다.",
"legacy.surface.61fcdf8c82cb687a": "미리 본 과제를 적용하시겠습니까?",
"legacy.surface.6233afa7701a7fb8": "표시된 항목 포함",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "흔들기",
"legacy.surface.7179ea0035fca783": "필터 지우기",
"legacy.surface.7188848298a0b500": "범위: 즐겨찾기",
+ "legacy.surface.71988c4d8e0803ba": "텍스트",
"legacy.surface.719e8555bf1abeba": "먼저 템플릿 단계를 사용하고, 파일을 가져오거나, 폴더를 수동으로 생성하세요.",
"legacy.surface.71b2e8d085cdca1e": "준비: 모든 확인이 통과되었습니다.",
"legacy.surface.7205aff54d71cb8b": "먼저 다른 Docker 폴더에서 폴더 설정을 복사하세요.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "미리보기:",
"legacy.surface.75bf7d9afc988cb6": "폴더는 이름별로 A부터 Z까지 정렬됩니다. 중첩된 폴더는 여전히 상위 분기 내에 남아 있습니다.",
"legacy.surface.75d599007fcf49af": "모든 회원",
- "legacy.surface.75d7a33335c4f033": "폴더 행에 사용되는 시작, 일시 중지 및 중지 색상을 설정합니다.",
"legacy.surface.7611fe57a952da4b": "추천합니다. 위험한 변경을 자동으로 차단합니다.",
"legacy.surface.764210a29b968296": "템플릿 카테고리",
"legacy.surface.764b74dea7f342ff": "축소된 미리보기에 하위 폴더가 표시될 때 칩으로 표시되는 중첩된 폴더 수준 수를 제어합니다.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM 테이블 본문",
"legacy.surface.8569ce7917f23eb6": "선택한 규칙 $1개를 삭제하시겠습니까?",
"legacy.surface.858e4ba7a29fd38b": "일시중지",
+ "legacy.surface.85b6c17862a72780": "WebUI 프로필",
"legacy.surface.85c83f4500bb9f2c": "VM 런타임 정보",
"legacy.surface.85e2efe37bad1a44": "이 작업에 적합한 항목이 없습니다.",
"legacy.surface.8600edb883562f2c": "LAN IP 마스크",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "현재 Docker 및 VM 폴더 설정을 스캔하십시오.",
"legacy.surface.916b423aa46d7493": "$1개의 권고 문제(깊이/빈 분기)가 감지되었지만 자동 복구를 위한 고아/주기 링크 오류는 없습니다.",
"legacy.surface.91f41a1797b442ab": "저장된 템플릿에서 $1을(를) 업데이트했습니다.",
- "legacy.surface.920b3e3ce5710d1d": "Docker, VM 및 대시보드 보기 전체에서 이 폴더의 상태 아이콘과 텍스트에 대한 사용자 정의 색상을 설정합니다.",
"legacy.surface.920e413c7d411b61": "상태",
"legacy.surface.9213c7f4046c085d": "주의를 끌기 위한 경고 임계값",
"legacy.surface.921b15089caf4ff8": "3. 규칙 순서 검토",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "보류 중인 업데이트가 이 폴더의 상태에 미치는 영향을 제어합니다.",
"legacy.surface.926e4eab47bf42ea": "실시간 일치: 정규식 패턴을 입력합니다.",
"legacy.surface.92c1cdfdf4cb9cf6": "활성화됨",
+ "legacy.surface.92cff14b4b7992dd": "각 명명된 프로필에 의해 열려 있는 다이렉트 폴더 멤버를 선택합니다.",
"legacy.surface.92ec9d61393d7064": "$1 정규식",
"legacy.surface.93aa7d6a1b93167d": "맞춤 아이콘을 찾을 수 없습니다. 시작하려면 아이콘을 업로드하세요.",
"legacy.surface.93b1554a829a9433": "3개 레벨",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "저장되지 않은 변경사항",
"legacy.surface.a71d508e50432f40": "홈랩",
"legacy.surface.a7248eeb45eb1ce2": "보통",
- "legacy.surface.a759d24fa1d32402": "모든 WebUI 열기",
"legacy.surface.a75ee6a28c39f865": "미리보기 및 확장된 행에 표시되는 순서를 제어하려면 하위 폴더를 드래그하세요.",
"legacy.surface.a76ac50faa110d7d": "가장 오래 생성된 폴더부터",
"legacy.surface.a76e13b9839270eb": "복원",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "드라이런",
"legacy.surface.d640c7421da06661": "이력서",
"legacy.surface.d66d27cf2aa666c8": "디스플레이 선택",
+ "legacy.surface.d693881b5ac21882": "--fvplus-폴더-상태-텍스트",
"legacy.surface.d696a35bdd1883da": "프로필",
"legacy.surface.d69b03bf0620f859": "기존 FolderView 설정을 가져옵니다.",
"legacy.surface.d6a226a96bdc60fe": "팝업 차단",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "비교",
"legacy.surface.e89b30aa1dc30a6a": "자원",
"legacy.surface.e89c3109e1f14c41": "대상 폴더",
+ "legacy.surface.e8b4d99f2d672a8e": "맞춤형 WebUI 프로필",
"legacy.surface.e8e470943fd0f0b6": "중첩된 폴더 순서",
"legacy.surface.e948f182d485afee": "지점 확장",
"legacy.surface.e94d3ee06ecf6aac": "내보내기 실패",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/docker.json
index b3c2aa9d..44b505f1 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maskējiet publiskos IP",
"docker.privacy.mask-network-interfaces": "Maskējiet tīkla saskarnes",
"docker.privacy.mask-external-urls": "Maskējiet ārējos URL",
- "docker.actions.capture-diagnostics": "Uzņemt lapas diagnostiku"
+ "docker.actions.capture-diagnostics": "Uzņemt lapas diagnostiku",
+ "docker.folder.hide": "Slēpt mapi",
+ "docker.folder.restore": "Atjaunot mapi",
+ "docker.folder.restore-hidden-branch": "Atjaunot slēpto zaru",
+ "docker.folder.hidden-indicator": "Slēpts",
+ "docker.folder.unnamed": "Mape",
+ "docker.folder.visibility-failed": "Mapes redzamības preferenci nevarēja saglabāt.",
+ "docker.visibility.hidden-folders": "Slēptās mapes",
+ "docker.visibility.restore-all": "Atjaunot visas slēptās mapes",
+ "docker.visibility.hidden-title": "Mape paslēpta",
+ "docker.visibility.folder-fallback": "Mape",
+ "docker.visibility.hidden-message": "tika paslēpts no Docker skata.",
+ "docker.visibility.undo": "Atsaukt",
+ "docker.visibility.all-hidden-title": "Visas mapju rindas ir paslēptas.",
+ "docker.visibility.all-hidden-help": "Atklājiet tos uz laiku vai atjaunojiet katru slēpto mapi.",
+ "docker.visibility.show-hidden": "Rādīt slēptās mapes",
+ "docker.webui-profiles.open": "Atveriet WebUI profilu",
+ "docker.webui-profiles.ready": "$1 ($2 no $3 gatavs)",
+ "docker.webui-profiles.manage": "Pārvaldiet WebUI profilus",
+ "docker.webui-profiles.none-title": "Nav gatavs WebUI",
+ "docker.webui-profiles.none-body": "Neviens no atlasītajiem konteineriem nedarbojas ar pieejamu WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/editor.json
index 153f44dd..6cb667e0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Izveidot projektu vienāds",
"editor.rules.templates.compose-project-placeholder": "Projekta nosaukums (piemērs: plašsaziņas līdzekļi)",
"editor.rules.templates.label-equals": "Docker etiķete ir vienāda",
- "editor.rules.templates.label-equals-placeholder": "Iezīmes atslēga=vērtība"
+ "editor.rules.templates.label-equals-placeholder": "Iezīmes atslēga=vērtība",
+ "editor.webui-profiles.title": "Pielāgoti WebUI profili",
+ "editor.webui-profiles.help": "Izveidojiet atkārtoti lietojamas palaišanas grupas no šīs mapes tiešajiem Docker dalībniekiem. Pašreizējās WebUI adreses un darbības stāvoklis tiek pārbaudīts tikai tad, kad tiek atvērts profils.",
+ "editor.webui-profiles.add": "Pievienot profilu",
+ "editor.webui-profiles.count": "$1 saglabāts(-i) profils(-i)",
+ "editor.webui-profiles.empty": "Vēl nav pielāgotu WebUI profilu.",
+ "editor.webui-profiles.name": "Profila nosaukums",
+ "editor.webui-profiles.duplicate": "Dublikāts",
+ "editor.webui-profiles.move-up": "Pārvietojieties uz augšu",
+ "editor.webui-profiles.move-down": "Pārvietojieties uz leju",
+ "editor.webui-profiles.delete": "Dzēst",
+ "editor.webui-profiles.search": "Meklēt mapes dalībniekus",
+ "editor.webui-profiles.select-webui": "Atlasiet visu, izmantojot WebUI",
+ "editor.webui-profiles.clear": "Notīrīt atlasi",
+ "editor.webui-profiles.webui-ready": "WebUI ir konfigurēts",
+ "editor.webui-profiles.webui-runtime": "WebUI pārbaudīts palaišanas laikā",
+ "editor.webui-profiles.unavailable": "Nav pieejams šajā mapē",
+ "editor.webui-profiles.no-members": "Nav pieejams neviens atbilstošs mapes dalībnieks.",
+ "editor.webui-profiles.error-name": "Katram WebUI profilam ir nepieciešams nosaukums.",
+ "editor.webui-profiles.error-duplicate": "WebUI profilu nosaukumiem šajā mapē ir jābūt unikāliem.",
+ "editor.webui-profiles.error-members": "Katrā WebUI profilā ir jāatlasa vismaz viens mapes dalībnieks.",
+ "editor.webui-profiles.limit": "Šī mape ir sasniegusi WebUI profila drošības ierobežojumu."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/legacy-surface.json
index 575cbe81..a0698e79 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/lv/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nav lietotņu izmaiņu",
"legacy.surface.26f0360c9dddc4d1": "Iepriekš iestatīts dzēsts",
"legacy.surface.270bca2990500a60": "Izvēlieties šīs mapes atrašanās vietu hierarhijā.",
+ "legacy.surface.273c468809bda389": "Iestatiet indikatora krāsas un neatkarīgo statusa teksta krāsu, ko izmanto mapes rindas.",
"legacy.surface.273f838c039ee10f": "FolderView Plus konstatēja, ka lapa Iestatījumi kļuva tukša pirms inicializācijas pabeigšanas.",
"legacy.surface.2753807153755a5e": "Apvienot (pievienot + atjaunināt)",
"legacy.surface.2847d4f584d08e0c": "Notiek Docker komandu skata ielāde...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Sākt filiāli ($1)",
"legacy.surface.32c919087b37ec2d": "Multivides kaudze",
"legacy.surface.334277e71d658735": "Pēc mērķa",
+ "legacy.surface.33541731c2a77842": "Iestatiet indikatora Sākts, Apturēts un Apturēts krāsas neatkarīgi no statusa teksta krāsas Docker, virtuālās mašīnas un informācijas paneļa skatos.",
"legacy.surface.3388c6d7238cdde6": "Spraudņa iestatījumu nosaukums",
"legacy.surface.33891d013966c572": "Atlasiet mapi lielapjoma piešķiršanai.",
"legacy.surface.33c5850b76f401e8": "Augstākā līmeņa mape",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Izvēlieties stāvokļa maiņu un tās kontrolējošos dalībniekus.",
"legacy.surface.613aa3b591bacd9a": "Kopējā krātuves (ar failiem nodrošināti diski): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron krāsas:",
+ "legacy.surface.61ea108a3718792d": "Izveidojiet nosauktas grupas, kas atver tikai atlasītos Docker konteinera WebUI.",
"legacy.surface.61f565ba05bd647d": "Šeit tiks automātiski iekļauti atbilstoši konteinera vai virtuālās mašīnas nosaukumi.",
"legacy.surface.61fcdf8c82cb687a": "Vai lietot priekšskatītos uzdevumus?",
"legacy.surface.6233afa7701a7fb8": "Iekļaut parādīto",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Pakustēties",
"legacy.surface.7179ea0035fca783": "Notīrīt filtrus",
"legacy.surface.7188848298a0b500": "Darbības joma: favorīti",
+ "legacy.surface.71988c4d8e0803ba": "Teksts",
"legacy.surface.719e8555bf1abeba": "Vispirms izmantojiet darbību Veidnes, importējiet failus vai izveidojiet mapes manuāli.",
"legacy.surface.71b2e8d085cdca1e": "Gatavs: visas pārbaudes ir izturētas.",
"legacy.surface.7205aff54d71cb8b": "Vispirms kopējiet mapes iestatījumus no citas Docker mapes.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Priekšskatījums:",
"legacy.surface.75bf7d9afc988cb6": "Mapes tiek kārtotas pēc nosaukuma no A līdz Z. Ligzdotas mapes joprojām paliek savā vecākzarā.",
"legacy.surface.75d599007fcf49af": "Visa dalība",
- "legacy.surface.75d7a33335c4f033": "Iestatiet sāktās, apturētās un apturētās krāsas, ko izmanto mapes rindas.",
"legacy.surface.7611fe57a952da4b": "Ieteicams. Automātiski bloķē riskantas izmaiņas.",
"legacy.surface.764210a29b968296": "Veidnes kategorija",
"legacy.surface.764b74dea7f342ff": "Kontrolē, cik ligzdotu mapju līmeņu tiek rādītas kā mikroshēmas, kad sakļautajā priekšskatījumā tiek rādītas pakārtotās mapes.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM tabulas korpuss",
"legacy.surface.8569ce7917f23eb6": "Vai dzēst $1 atlasīto(-s) kārtulu(-as)?",
"legacy.surface.858e4ba7a29fd38b": "Pauze",
+ "legacy.surface.85b6c17862a72780": "WebUI profili",
"legacy.surface.85c83f4500bb9f2c": "VM izpildlaika informācija",
"legacy.surface.85e2efe37bad1a44": "Šai darbībai nav piemērotu vienumu.",
"legacy.surface.8600edb883562f2c": "Maskējiet LAN IP",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Skenējiet pašreizējo Docker un VM mapes iestatījumus.",
"legacy.surface.916b423aa46d7493": "Konstatēta $1 konsultatīva(-as) problēma(-as) (dziļums/tukšs atzars), taču bez automātiskās labošanas kļūdas/cikla saites kļūdas.",
"legacy.surface.91f41a1797b442ab": "Atjaunināts $1 no saglabātās veidnes.",
- "legacy.surface.920b3e3ce5710d1d": "Iestatiet pielāgotas krāsas šīs mapes statusa ikonai un tekstam Docker, virtuālās mašīnas un informācijas paneļa skatos.",
"legacy.surface.920e413c7d411b61": "Statuss",
"legacy.surface.9213c7f4046c085d": "Brīdinājuma slieksnis uzmanībai",
"legacy.surface.921b15089caf4ff8": "3. Pārskatiet noteikumu secību",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Kontrolē, kā neapstiprinātie atjauninājumi ietekmē šīs mapes stāvokli.",
"legacy.surface.926e4eab47bf42ea": "Tiešraides atbilstības: ievadiet regulāru izteiksmi.",
"legacy.surface.92c1cdfdf4cb9cf6": "Iespējots",
+ "legacy.surface.92cff14b4b7992dd": "Atlasiet tiešās mapes dalībniekus, ko atver katrs nosauktais profils.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Netika atrasta neviena pielāgota ikona. Lai sāktu, augšupielādējiet ikonu.",
"legacy.surface.93b1554a829a9433": "3 līmeņi",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Nesaglabātas izmaiņas",
"legacy.surface.a71d508e50432f40": "Mājas laboratorija",
"legacy.surface.a7248eeb45eb1ce2": "Normāls",
- "legacy.surface.a759d24fa1d32402": "Atveriet visas WebUI",
"legacy.surface.a75ee6a28c39f865": "Velciet pakārtotās mapes, lai kontrolētu priekšskatījumos un izvērstajās rindās redzamo secību.",
"legacy.surface.a76ac50faa110d7d": "Vispirms vecākās izveidotās mapes",
"legacy.surface.a76e13b9839270eb": "Atjaunot",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Sausā skrējiens",
"legacy.surface.d640c7421da06661": "Atsākt",
"legacy.surface.d66d27cf2aa666c8": "Displeja izvēles",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Profils",
"legacy.surface.d69b03bf0620f859": "Ievadiet esošu FolderView iestatījumu.",
"legacy.surface.d6a226a96bdc60fe": "Bloķēt uznirstošos logus",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Salīdziniet",
"legacy.surface.e89b30aa1dc30a6a": "Resursi",
"legacy.surface.e89c3109e1f14c41": "Mērķa mape",
+ "legacy.surface.e8b4d99f2d672a8e": "Pielāgoti WebUI profili",
"legacy.surface.e8e470943fd0f0b6": "Ligzdoto mapju secība",
"legacy.surface.e948f182d485afee": "Paplašināt filiāli",
"legacy.surface.e94d3ee06ecf6aac": "Eksportēšana neizdevās",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/docker.json
index 9fd3d049..9148fcbf 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maske offentlige IP-er",
"docker.privacy.mask-network-interfaces": "Maske nettverksgrensesnitt",
"docker.privacy.mask-external-urls": "Maske eksterne nettadresser",
- "docker.actions.capture-diagnostics": "Registreringssidediagnostikk"
+ "docker.actions.capture-diagnostics": "Registreringssidediagnostikk",
+ "docker.folder.hide": "Skjul mappe",
+ "docker.folder.restore": "Gjenopprett mappe",
+ "docker.folder.restore-hidden-branch": "Gjenopprett skjult gren",
+ "docker.folder.hidden-indicator": "Skjult",
+ "docker.folder.unnamed": "Mappe",
+ "docker.folder.visibility-failed": "Innstillingen for mappesynlighet kunne ikke lagres.",
+ "docker.visibility.hidden-folders": "Skjulte mapper",
+ "docker.visibility.restore-all": "Gjenopprett alle skjulte mapper",
+ "docker.visibility.hidden-title": "Mappe skjult",
+ "docker.visibility.folder-fallback": "Mappe",
+ "docker.visibility.hidden-message": "ble skjult fra Docker-visningen.",
+ "docker.visibility.undo": "Angre",
+ "docker.visibility.all-hidden-title": "Alle mapperader er skjult.",
+ "docker.visibility.all-hidden-help": "Avslør dem midlertidig eller gjenopprett hver skjulte mappe.",
+ "docker.visibility.show-hidden": "Vis skjulte mapper",
+ "docker.webui-profiles.open": "Åpne WebUI-profilen",
+ "docker.webui-profiles.ready": "$1 ($2 av $3 klar)",
+ "docker.webui-profiles.manage": "Administrer WebUI-profiler",
+ "docker.webui-profiles.none-title": "Ingen WebUIer er klare",
+ "docker.webui-profiles.none-body": "Ingen av de valgte beholderne kjører med et tilgjengelig WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/editor.json
index 3b881df4..864b3815 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Komponer prosjekt lik",
"editor.rules.templates.compose-project-placeholder": "Prosjektnavn (eksempel: media)",
"editor.rules.templates.label-equals": "Docker-etikett er lik",
- "editor.rules.templates.label-equals-placeholder": "Etikett nøkkel=verdi"
+ "editor.rules.templates.label-equals-placeholder": "Etikett nøkkel=verdi",
+ "editor.webui-profiles.title": "Egendefinerte WebUI-profiler",
+ "editor.webui-profiles.help": "Opprett gjenbrukbare lanseringsgrupper fra denne mappens direkte Docker-medlemmer. Gjeldende WebUI-adresser og kjørestatus kontrolleres kun når en profil åpnes.",
+ "editor.webui-profiles.add": "Legg til profil",
+ "editor.webui-profiles.count": "$1 lagrede profil(er)",
+ "editor.webui-profiles.empty": "Ingen egendefinerte WebUI-profiler ennå.",
+ "editor.webui-profiles.name": "Profilnavn",
+ "editor.webui-profiles.duplicate": "Duplikat",
+ "editor.webui-profiles.move-up": "Flytt opp",
+ "editor.webui-profiles.move-down": "Flytt ned",
+ "editor.webui-profiles.delete": "Slett",
+ "editor.webui-profiles.search": "Søk i mappemedlemmer",
+ "editor.webui-profiles.select-webui": "Velg alle med WebUI",
+ "editor.webui-profiles.clear": "Fjern valg",
+ "editor.webui-profiles.webui-ready": "WebUI konfigurert",
+ "editor.webui-profiles.webui-runtime": "WebUI sjekket ved lansering",
+ "editor.webui-profiles.unavailable": "Ikke tilgjengelig i denne mappen",
+ "editor.webui-profiles.no-members": "Ingen samsvarende mappemedlemmer er tilgjengelige.",
+ "editor.webui-profiles.error-name": "Hver WebUI-profil trenger et navn.",
+ "editor.webui-profiles.error-duplicate": "WebUI-profilnavn må være unike i denne mappen.",
+ "editor.webui-profiles.error-members": "Hver WebUI-profil må velge minst ett mappemedlem.",
+ "editor.webui-profiles.limit": "Denne mappen har nådd sikkerhetsgrensen for WebUI-profilen."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/legacy-surface.json
index 89c126f5..c80511dc 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nb/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Ingen appendringer",
"legacy.surface.26f0360c9dddc4d1": "Forhåndsinnstilling slettet",
"legacy.surface.270bca2990500a60": "Velg hvor denne mappen bor i hierarkiet.",
+ "legacy.surface.273c468809bda389": "Angi indikatorfarger og den uavhengige statustekstfargen som brukes av mapperader.",
"legacy.surface.273f838c039ee10f": "FolderView Plus oppdaget at Innstillinger-siden ble tom før initialiseringen ble fullført.",
"legacy.surface.2753807153755a5e": "Slå sammen (legg til + oppdater)",
"legacy.surface.2847d4f584d08e0c": "Laster Docker-kommandovisning...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Start gren ($1)",
"legacy.surface.32c919087b37ec2d": "Mediestakk",
"legacy.surface.334277e71d658735": "Etter målet",
+ "legacy.surface.33541731c2a77842": "Angi Startet, Pauset og Stoppet-indikatorfargene uavhengig av statustekstfargen på tvers av Docker-, VM-er og Dashboard-visninger.",
"legacy.surface.3388c6d7238cdde6": "Tittel på plugininnstillinger",
"legacy.surface.33891d013966c572": "Velg en mappe for massetildeling.",
"legacy.surface.33c5850b76f401e8": "mappe på øverste nivå",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Velg tilstandsendringen og hvilke medlemmer den kontrollerer.",
"legacy.surface.613aa3b591bacd9a": "Totalt lagringsplass (filstøttede disker): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron farger:",
+ "legacy.surface.61ea108a3718792d": "Opprett navngitte grupper som bare åpner utvalgte Docker-beholder WebUIer.",
"legacy.surface.61f565ba05bd647d": "Matchende container- eller VM-navn blir automatisk inkludert her.",
"legacy.surface.61fcdf8c82cb687a": "Vil du bruke forhåndsviste oppgaver?",
"legacy.surface.6233afa7701a7fb8": "Inkluder vist",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Vrikk",
"legacy.surface.7179ea0035fca783": "Tøm filtre",
"legacy.surface.7188848298a0b500": "Omfang: favoritter",
+ "legacy.surface.71988c4d8e0803ba": "Tekst",
"legacy.surface.719e8555bf1abeba": "Bruk maler-trinnet, importer filer eller opprett mapper manuelt først.",
"legacy.surface.71b2e8d085cdca1e": "Klar: alle sjekker bestått.",
"legacy.surface.7205aff54d71cb8b": "Kopier mappeinnstillinger fra en annen Docker-mappe først.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Forhåndsvisning:",
"legacy.surface.75bf7d9afc988cb6": "Mapper er sortert etter navn fra A til Å. Nestede mapper forblir fortsatt i den overordnede grenen.",
"legacy.surface.75d599007fcf49af": "Alt medlemskap",
- "legacy.surface.75d7a33335c4f033": "Angi fargene for start, pause og stoppet som brukes av mapperader.",
"legacy.surface.7611fe57a952da4b": "Anbefalt. Blokkerer risikofylte endringer automatisk.",
"legacy.surface.764210a29b968296": "Malkategori",
"legacy.surface.764b74dea7f342ff": "Kontrollerer hvor mange nestede mappenivåer som vises som brikker når undermapper vises i den skjulte forhåndsvisningen.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM bordkropp",
"legacy.surface.8569ce7917f23eb6": "Vil du slette $1 valgte regel(er)?",
"legacy.surface.858e4ba7a29fd38b": "Pause",
+ "legacy.surface.85b6c17862a72780": "WebUI-profiler",
"legacy.surface.85c83f4500bb9f2c": "VM kjøretidsinformasjon",
"legacy.surface.85e2efe37bad1a44": "Ingen kvalifiserte elementer for denne handlingen.",
"legacy.surface.8600edb883562f2c": "Maske LAN-IP-er",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Skann ditt nåværende Docker- og VM-mappeoppsett.",
"legacy.surface.916b423aa46d7493": "Oppdaget $1 rådgivende problem(r) (dybde/tom gren), men ingen foreldreløse/sykkelkoblingsfeil for autoreparasjon.",
"legacy.surface.91f41a1797b442ab": "Oppdatert $1 fra lagret mal.",
- "legacy.surface.920b3e3ce5710d1d": "Angi egendefinerte farger for denne mappens statusikon og tekst på tvers av Docker-, VM-er og Dashboard-visninger.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Advarselsterskel for oppmerksomhet",
"legacy.surface.921b15089caf4ff8": "3. Gjennomgå regelrekkefølgen",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Styrer hvordan ventende oppdateringer påvirker denne mappens helse.",
"legacy.surface.926e4eab47bf42ea": "Live-kamper: skriv inn et regex-mønster.",
"legacy.surface.92c1cdfdf4cb9cf6": "Aktivert",
+ "legacy.surface.92cff14b4b7992dd": "Velg de direkte mappemedlemmene som åpnes av hver navngitte profil.",
"legacy.surface.92ec9d61393d7064": "$1 regulært uttrykk",
"legacy.surface.93aa7d6a1b93167d": "Fant ingen egendefinerte ikoner. Last opp et ikon for å komme i gang.",
"legacy.surface.93b1554a829a9433": "3 nivåer",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Ulagrede endringer",
"legacy.surface.a71d508e50432f40": "Hjemmelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Åpne alle WebUIer",
"legacy.surface.a75ee6a28c39f865": "Dra undermapper for å kontrollere rekkefølgen som vises i forhåndsvisninger og utvidede rader.",
"legacy.surface.a76ac50faa110d7d": "Eldste opprettede mapper først",
"legacy.surface.a76e13b9839270eb": "Gjenopprett",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Tørrløp",
"legacy.surface.d640c7421da06661": "Fortsett",
"legacy.surface.d66d27cf2aa666c8": "Vis valg",
+ "legacy.surface.d693881b5ac21882": "--fvplus-mappe-status-tekst",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Ta inn et eksisterende FolderView-oppsett.",
"legacy.surface.d6a226a96bdc60fe": "Blokker popup-vinduer",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Sammenlign",
"legacy.surface.e89b30aa1dc30a6a": "Ressurser",
"legacy.surface.e89c3109e1f14c41": "Målmappe",
+ "legacy.surface.e8b4d99f2d672a8e": "Egendefinerte WebUI-profiler",
"legacy.surface.e8e470943fd0f0b6": "Nestet mapperekkefølge",
"legacy.surface.e948f182d485afee": "Utvid grenen",
"legacy.surface.e94d3ee06ecf6aac": "Eksporten mislyktes",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/docker.json
index 07b31828..114ee562 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Verberg openbare IPs",
"docker.privacy.mask-network-interfaces": "Verberg netwerkinterfaces",
"docker.privacy.mask-external-urls": "Verberg externe URL's",
- "docker.actions.capture-diagnostics": "Paginadiagnostiek vastleggen"
+ "docker.actions.capture-diagnostics": "Paginadiagnostiek vastleggen",
+ "docker.folder.hide": "Map verbergen",
+ "docker.folder.restore": "Map herstellen",
+ "docker.folder.restore-hidden-branch": "Herstel verborgen tak",
+ "docker.folder.hidden-indicator": "Verborgen",
+ "docker.folder.unnamed": "Map",
+ "docker.folder.visibility-failed": "De voorkeur voor mapzichtbaarheid kan niet worden opgeslagen.",
+ "docker.visibility.hidden-folders": "Verborgen mappen",
+ "docker.visibility.restore-all": "Herstel alle verborgen mappen",
+ "docker.visibility.hidden-title": "Map verborgen",
+ "docker.visibility.folder-fallback": "Map",
+ "docker.visibility.hidden-message": "was verborgen in de Docker-weergave.",
+ "docker.visibility.undo": "Ongedaan maken",
+ "docker.visibility.all-hidden-title": "Alle maprijen zijn verborgen.",
+ "docker.visibility.all-hidden-help": "Onthul ze tijdelijk of herstel elke verborgen map.",
+ "docker.visibility.show-hidden": "Toon verborgen mappen",
+ "docker.webui-profiles.open": "Open WebUI-profiel",
+ "docker.webui-profiles.ready": "$1 ($2 van $3 gereed)",
+ "docker.webui-profiles.manage": "Beheer WebUI-profielen",
+ "docker.webui-profiles.none-title": "Geen WebUI's gereed",
+ "docker.webui-profiles.none-body": "Geen van de geselecteerde containers draait met een beschikbare WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/editor.json
index dd890c5a..06673576 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Componeer project is gelijk aan",
"editor.rules.templates.compose-project-placeholder": "Projectnaam (bijvoorbeeld: media)",
"editor.rules.templates.label-equals": "Dockerlabel is gelijk aan",
- "editor.rules.templates.label-equals-placeholder": "Labelsleutel=waarde"
+ "editor.rules.templates.label-equals-placeholder": "Labelsleutel=waarde",
+ "editor.webui-profiles.title": "Aangepaste WebUI-profielen",
+ "editor.webui-profiles.help": "Maak herbruikbare startgroepen van de directe Docker-leden van deze map. Huidige WebUI-adressen en actieve status worden alleen gecontroleerd wanneer een profiel wordt geopend.",
+ "editor.webui-profiles.add": "Profiel toevoegen",
+ "editor.webui-profiles.count": "$1 opgeslagen profiel(en)",
+ "editor.webui-profiles.empty": "Nog geen aangepaste WebUI-profielen.",
+ "editor.webui-profiles.name": "Profielnaam",
+ "editor.webui-profiles.duplicate": "Duplicaat",
+ "editor.webui-profiles.move-up": "Ga omhoog",
+ "editor.webui-profiles.move-down": "Ga naar beneden",
+ "editor.webui-profiles.delete": "Verwijderen",
+ "editor.webui-profiles.search": "Zoek mapleden",
+ "editor.webui-profiles.select-webui": "Selecteer alles met WebUI",
+ "editor.webui-profiles.clear": "Duidelijke selectie",
+ "editor.webui-profiles.webui-ready": "WebUI geconfigureerd",
+ "editor.webui-profiles.webui-runtime": "WebUI gecontroleerd bij opstarten",
+ "editor.webui-profiles.unavailable": "Niet beschikbaar in deze map",
+ "editor.webui-profiles.no-members": "Er zijn geen overeenkomende mapleden beschikbaar.",
+ "editor.webui-profiles.error-name": "Elk WebUI-profiel heeft een naam nodig.",
+ "editor.webui-profiles.error-duplicate": "WebUI-profielnamen moeten uniek zijn binnen deze map.",
+ "editor.webui-profiles.error-members": "Elk WebUI-profiel moet ten minste één maplid selecteren.",
+ "editor.webui-profiles.limit": "Deze map heeft de veiligheidslimiet voor het WebUI-profiel bereikt."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/legacy-surface.json
index abe82f4f..b65ae250 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/nl/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Geen app-wijzigingen",
"legacy.surface.26f0360c9dddc4d1": "Voorinstelling verwijderd",
"legacy.surface.270bca2990500a60": "Kies waar deze map zich in de hiërarchie bevindt.",
+ "legacy.surface.273c468809bda389": "Stel indicatorkleuren en de onafhankelijke statustekstkleur in die door maprijen wordt gebruikt.",
"legacy.surface.273f838c039ee10f": "FolderView Plus heeft gedetecteerd dat de pagina Instellingen leeg werd voordat de initialisatie was voltooid.",
"legacy.surface.2753807153755a5e": "Samenvoegen (toevoegen + bijwerken)",
"legacy.surface.2847d4f584d08e0c": "Docker-opdrachtweergave laden...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Tak starten ($1)",
"legacy.surface.32c919087b37ec2d": "Mediastapel",
"legacy.surface.334277e71d658735": "Na doel",
+ "legacy.surface.33541731c2a77842": "Stel de indicatorkleuren Gestart, Gepauzeerd en Gestopt in, onafhankelijk van de statustekstkleur in de Docker-, VM's- en Dashboard-weergaven.",
"legacy.surface.3388c6d7238cdde6": "Titel van plug-ininstellingen",
"legacy.surface.33891d013966c572": "Selecteer een map voor bulktoewijzing.",
"legacy.surface.33c5850b76f401e8": "Map op het hoogste niveau",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Kies de statuswijziging en welke leden deze controleert.",
"legacy.surface.613aa3b591bacd9a": "Totaal opslag (schijven met bestandsback-up): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron-kleuren:",
+ "legacy.surface.61ea108a3718792d": "Maak benoemde groepen die alleen geselecteerde Docker-container WebUI's openen.",
"legacy.surface.61f565ba05bd647d": "Overeenkomende container- of VM-namen worden hier automatisch opgenomen.",
"legacy.surface.61fcdf8c82cb687a": "Voorbeeldopdrachten toepassen?",
"legacy.surface.6233afa7701a7fb8": "Inclusief weergegeven",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Wiebelen",
"legacy.surface.7179ea0035fca783": "Wis filters",
"legacy.surface.7188848298a0b500": "Omvang: favorieten",
+ "legacy.surface.71988c4d8e0803ba": "Tekst",
"legacy.surface.719e8555bf1abeba": "Gebruik de stap Sjablonen, importeer bestanden of maak eerst handmatig mappen.",
"legacy.surface.71b2e8d085cdca1e": "Klaar: alle controles zijn geslaagd.",
"legacy.surface.7205aff54d71cb8b": "Kopieer eerst de mapinstellingen uit een andere Docker-map.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Voorbeeld:",
"legacy.surface.75bf7d9afc988cb6": "Mappen worden op naam gesorteerd van A tot Z. Geneste mappen blijven nog steeds binnen hun bovenliggende vertakking.",
"legacy.surface.75d599007fcf49af": "Alle lidmaatschappen",
- "legacy.surface.75d7a33335c4f033": "Stel de gestarte, gepauzeerde en gestopte kleuren in die door maprijen worden gebruikt.",
"legacy.surface.7611fe57a952da4b": "Aanbevolen. Blokkeert risicovolle wijzigingen automatisch.",
"legacy.surface.764210a29b968296": "Sjablooncategorie",
"legacy.surface.764b74dea7f342ff": "Bepaalt hoeveel geneste mapniveaus als chips verschijnen wanneer onderliggende mappen worden weergegeven in het samengevouwen voorbeeld.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM-tabellichaam",
"legacy.surface.8569ce7917f23eb6": "$1 geselecteerde regel(s) verwijderen?",
"legacy.surface.858e4ba7a29fd38b": "Pauze",
+ "legacy.surface.85b6c17862a72780": "WebUI-profielen",
"legacy.surface.85c83f4500bb9f2c": "VM-runtime-informatie",
"legacy.surface.85e2efe37bad1a44": "Er zijn geen in aanmerking komende items voor deze actie.",
"legacy.surface.8600edb883562f2c": "Masker LAN IP's",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Scan uw huidige Docker- en VM-mapinstellingen.",
"legacy.surface.916b423aa46d7493": "$1 adviesprobleem(en) gedetecteerd (diepte/lege vertakking), maar geen wees-/cycluskoppelingsfouten die automatisch moeten worden gerepareerd.",
"legacy.surface.91f41a1797b442ab": "$1 bijgewerkt vanuit opgeslagen sjabloon.",
- "legacy.surface.920b3e3ce5710d1d": "Stel aangepaste kleuren in voor het statuspictogram en de tekst van deze map in de Docker-, VM's- en Dashboard-weergaven.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Waarschuw drempel voor aandacht",
"legacy.surface.921b15089caf4ff8": "3. Controleer de regelvolgorde",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Bepaalt hoe lopende updates de gezondheid van deze map beïnvloeden.",
"legacy.surface.926e4eab47bf42ea": "Live overeenkomsten: voer een regex-patroon in.",
"legacy.surface.92c1cdfdf4cb9cf6": "Ingeschakeld",
+ "legacy.surface.92cff14b4b7992dd": "Selecteer de directe mapleden die door elk benoemd profiel zijn geopend.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Geen aangepaste pictogrammen gevonden. Upload een pictogram om aan de slag te gaan.",
"legacy.surface.93b1554a829a9433": "3 niveaus",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Niet-opgeslagen wijzigingen",
"legacy.surface.a71d508e50432f40": "Thuislab",
"legacy.surface.a7248eeb45eb1ce2": "Normaal",
- "legacy.surface.a759d24fa1d32402": "Open alle WebUI's",
"legacy.surface.a75ee6a28c39f865": "Sleep onderliggende mappen om de volgorde te bepalen die wordt weergegeven in voorbeelden en uitgevouwen rijen.",
"legacy.surface.a76ac50faa110d7d": "Oudst aangemaakte mappen eerst",
"legacy.surface.a76e13b9839270eb": "Herstellen",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Drooglopen",
"legacy.surface.d640c7421da06661": "Hervatten",
"legacy.surface.d66d27cf2aa666c8": "Keuzes weergeven",
+ "legacy.surface.d693881b5ac21882": "--fvplus-mapstatus-tekst",
"legacy.surface.d696a35bdd1883da": "Profiel",
"legacy.surface.d69b03bf0620f859": "Breng een bestaande FolderView-installatie in.",
"legacy.surface.d6a226a96bdc60fe": "Blokkeer pop-ups",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Vergelijk",
"legacy.surface.e89b30aa1dc30a6a": "Hulpbronnen",
"legacy.surface.e89c3109e1f14c41": "Doelmap",
+ "legacy.surface.e8b4d99f2d672a8e": "Aangepaste WebUI-profielen",
"legacy.surface.e8e470943fd0f0b6": "Geneste mapvolgorde",
"legacy.surface.e948f182d485afee": "Vertakking uitbreiden",
"legacy.surface.e94d3ee06ecf6aac": "Exporteren is mislukt",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/docker.json
index edeacd22..8357b4af 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Ukryj publiczne IPs",
"docker.privacy.mask-network-interfaces": "Ukryj interfejsy sieciowe",
"docker.privacy.mask-external-urls": "Maskuj zewnętrzne adresy URL",
- "docker.actions.capture-diagnostics": "Przechwytywanie diagnostyki strony"
+ "docker.actions.capture-diagnostics": "Przechwytywanie diagnostyki strony",
+ "docker.folder.hide": "Ukryj folder",
+ "docker.folder.restore": "Przywróć folder",
+ "docker.folder.restore-hidden-branch": "Przywróć ukrytą gałąź",
+ "docker.folder.hidden-indicator": "Ukryty",
+ "docker.folder.unnamed": "Folder",
+ "docker.folder.visibility-failed": "Nie można zapisać preferencji widoczności folderu.",
+ "docker.visibility.hidden-folders": "Ukryte foldery",
+ "docker.visibility.restore-all": "Przywróć wszystkie ukryte foldery",
+ "docker.visibility.hidden-title": "Folder ukryty",
+ "docker.visibility.folder-fallback": "Folder",
+ "docker.visibility.hidden-message": "został ukryty w widoku Dockera.",
+ "docker.visibility.undo": "Cofnij",
+ "docker.visibility.all-hidden-title": "Wszystkie wiersze folderów są ukryte.",
+ "docker.visibility.all-hidden-help": "Odkryj je tymczasowo lub przywróć każdy ukryty folder.",
+ "docker.visibility.show-hidden": "Pokaż ukryte foldery",
+ "docker.webui-profiles.open": "Otwórz profil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 z $3 gotowy)",
+ "docker.webui-profiles.manage": "Zarządzaj profilami WebUI",
+ "docker.webui-profiles.none-title": "Brak gotowych interfejsów WebUI",
+ "docker.webui-profiles.none-body": "Żaden z wybranych kontenerów nie działa z dostępnym interfejsem WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/editor.json
index f097e162..a3c1ecc4 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Stworzenie projektu równa się",
"editor.rules.templates.compose-project-placeholder": "Nazwa projektu (przykład: media)",
"editor.rules.templates.label-equals": "Etykieta okna dokowanego równa się",
- "editor.rules.templates.label-equals-placeholder": "Klucz etykiety=wartość"
+ "editor.rules.templates.label-equals-placeholder": "Klucz etykiety=wartość",
+ "editor.webui-profiles.title": "Niestandardowe profile WebUI",
+ "editor.webui-profiles.help": "Utwórz grupy uruchamiania wielokrotnego użytku z bezpośrednich członków Dockera tego folderu. Bieżące adresy WebUI i stan działania są sprawdzane tylko po otwarciu profilu.",
+ "editor.webui-profiles.add": "Dodaj profil",
+ "editor.webui-profiles.count": "$1 zapisane profile",
+ "editor.webui-profiles.empty": "Nie ma jeszcze niestandardowych profili WebUI.",
+ "editor.webui-profiles.name": "Nazwa profilu",
+ "editor.webui-profiles.duplicate": "Duplikat",
+ "editor.webui-profiles.move-up": "Przesuń się w górę",
+ "editor.webui-profiles.move-down": "Przesuń w dół",
+ "editor.webui-profiles.delete": "Usuń",
+ "editor.webui-profiles.search": "Wyszukaj członków folderu",
+ "editor.webui-profiles.select-webui": "Wybierz wszystko za pomocą WebUI",
+ "editor.webui-profiles.clear": "Wyczyść wybór",
+ "editor.webui-profiles.webui-ready": "Skonfigurowano interfejs WebUI",
+ "editor.webui-profiles.webui-runtime": "WebUI sprawdzane przy uruchomieniu",
+ "editor.webui-profiles.unavailable": "Niedostępne w tym folderze",
+ "editor.webui-profiles.no-members": "Brak dostępnych pasujących członków folderu.",
+ "editor.webui-profiles.error-name": "Każdy profil WebUI potrzebuje nazwy.",
+ "editor.webui-profiles.error-duplicate": "Nazwy profili WebUI muszą być unikalne w tym folderze.",
+ "editor.webui-profiles.error-members": "Każdy profil WebUI musi wybierać co najmniej jednego członka folderu.",
+ "editor.webui-profiles.limit": "Ten folder osiągnął limit bezpieczeństwa profilu WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/legacy-surface.json
index 66f0d5b9..1a21f67b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pl/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Żadnych zmian w aplikacji",
"legacy.surface.26f0360c9dddc4d1": "Ustawienie wstępne usunięte",
"legacy.surface.270bca2990500a60": "Wybierz miejsce, w którym ten folder ma się znajdować w hierarchii.",
+ "legacy.surface.273c468809bda389": "Ustaw kolory wskaźników i niezależny kolor tekstu statusu używany w wierszach folderów.",
"legacy.surface.273f838c039ee10f": "FolderView Plus wykrył, że strona Ustawienia stała się pusta przed zakończeniem inicjalizacji.",
"legacy.surface.2753807153755a5e": "Scal (dodaj + zaktualizuj)",
"legacy.surface.2847d4f584d08e0c": "Ładowanie widoku poleceń Dockera...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Uruchom gałąź ($1)",
"legacy.surface.32c919087b37ec2d": "Stos multimediów",
"legacy.surface.334277e71d658735": "Po celu",
+ "legacy.surface.33541731c2a77842": "Ustaw kolory wskaźników Rozpoczęte, Wstrzymane i Zatrzymane niezależnie od koloru tekstu stanu w widokach Docker, maszyn wirtualnych i pulpitu nawigacyjnego.",
"legacy.surface.3388c6d7238cdde6": "Tytuł ustawień wtyczki",
"legacy.surface.33891d013966c572": "Wybierz folder do przypisania zbiorczego.",
"legacy.surface.33c5850b76f401e8": "Folder najwyższego poziomu",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Wybierz zmianę stanu i elementy członkowskie, które kontroluje.",
"legacy.surface.613aa3b591bacd9a": "Łączna ilość miejsca na dysku (dyski z kopią zapasową plików): $1",
"legacy.surface.61c56bacd53c2dfa": "Kolory jodełki:",
+ "legacy.surface.61ea108a3718792d": "Utwórz nazwane grupy, które otwierają tylko wybrane interfejsy WebUI kontenera Docker.",
"legacy.surface.61f565ba05bd647d": "Pasujące nazwy kontenerów lub maszyn wirtualnych zostaną tutaj automatycznie uwzględnione.",
"legacy.surface.61fcdf8c82cb687a": "Zastosować podgląd projektów?",
"legacy.surface.6233afa7701a7fb8": "Uwzględnij pokazane",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Poruszaj się",
"legacy.surface.7179ea0035fca783": "Wyczyść filtry",
"legacy.surface.7188848298a0b500": "Zakres: ulubione",
+ "legacy.surface.71988c4d8e0803ba": "Tekst",
"legacy.surface.719e8555bf1abeba": "Najpierw skorzystaj z kroku Szablony, zaimportuj pliki lub utwórz foldery ręcznie.",
"legacy.surface.71b2e8d085cdca1e": "Gotowy: wszystkie kontrole przeszły pomyślnie.",
"legacy.surface.7205aff54d71cb8b": "Najpierw skopiuj ustawienia folderu z innego folderu Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Podgląd:",
"legacy.surface.75bf7d9afc988cb6": "Foldery są sortowane według nazwy od A do Z. Zagnieżdżone foldery nadal pozostają w swojej gałęzi nadrzędnej.",
"legacy.surface.75d599007fcf49af": "Całe członkostwo",
- "legacy.surface.75d7a33335c4f033": "Ustaw kolory rozpoczęte, wstrzymane i zatrzymane używane w wierszach folderów.",
"legacy.surface.7611fe57a952da4b": "Zalecane. Automatycznie blokuje ryzykowne zmiany.",
"legacy.surface.764210a29b968296": "Kategoria szablonu",
"legacy.surface.764b74dea7f342ff": "Kontroluje liczbę zagnieżdżonych poziomów folderów wyświetlanych jako elementy, gdy foldery podrzędne są wyświetlane w zwiniętym podglądzie.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Treść tabeli maszyny wirtualnej",
"legacy.surface.8569ce7917f23eb6": "Usunąć $1 wybrane reguły?",
"legacy.surface.858e4ba7a29fd38b": "Pauza",
+ "legacy.surface.85b6c17862a72780": "Profile WebUI",
"legacy.surface.85c83f4500bb9f2c": "Informacje o czasie wykonywania maszyny wirtualnej",
"legacy.surface.85e2efe37bad1a44": "Brak elementów kwalifikujących się do tej akcji.",
"legacy.surface.8600edb883562f2c": "Maskuj adresy IP sieci LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Przeskanuj bieżącą konfigurację folderów Docker i VM.",
"legacy.surface.916b423aa46d7493": "Wykryto $1 problemy doradcze (głębokość/pusta gałąź), ale nie ma błędów łącza sierocego/cyklu do automatycznej naprawy.",
"legacy.surface.91f41a1797b442ab": "Zaktualizowano $1 z zapisanego szablonu.",
- "legacy.surface.920b3e3ce5710d1d": "Ustaw niestandardowe kolory ikony stanu i tekstu tego folderu w widokach Docker, maszyn wirtualnych i panelu kontrolnym.",
"legacy.surface.920e413c7d411b61": "Stan",
"legacy.surface.9213c7f4046c085d": "Ostrzegaj próg uwagi",
"legacy.surface.921b15089caf4ff8": "3. Przejrzyj kolejność reguł",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Kontroluje wpływ oczekujących aktualizacji na stan tego folderu.",
"legacy.surface.926e4eab47bf42ea": "Mecze na żywo: wprowadź wzór wyrażenia regularnego.",
"legacy.surface.92c1cdfdf4cb9cf6": "Włączone",
+ "legacy.surface.92cff14b4b7992dd": "Wybierz bezpośrednich członków folderu otwartych przez każdy nazwany profil.",
"legacy.surface.92ec9d61393d7064": "Wyrażenie regularne $1",
"legacy.surface.93aa7d6a1b93167d": "Nie znaleziono niestandardowych ikon. Aby rozpocząć, prześlij ikonę.",
"legacy.surface.93b1554a829a9433": "3 poziomy",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Niezapisane zmiany",
"legacy.surface.a71d508e50432f40": "Laboratorium domowe",
"legacy.surface.a7248eeb45eb1ce2": "Normalne",
- "legacy.surface.a759d24fa1d32402": "Otwórz wszystkie interfejsy WebUI",
"legacy.surface.a75ee6a28c39f865": "Przeciągnij foldery podrzędne, aby kontrolować kolejność pokazywania podglądów i rozwiniętych wierszy.",
"legacy.surface.a76ac50faa110d7d": "Najpierw utworzono najstarsze foldery",
"legacy.surface.a76e13b9839270eb": "Przywróć",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Praca na sucho",
"legacy.surface.d640c7421da06661": "Wznów",
"legacy.surface.d66d27cf2aa666c8": "Wyświetl opcje",
+ "legacy.surface.d693881b5ac21882": "--fvplus-tekst-statusu-folderu",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Przenieś istniejącą konfigurację FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Blokuj wyskakujące okienka",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Porównaj",
"legacy.surface.e89b30aa1dc30a6a": "Zasoby",
"legacy.surface.e89c3109e1f14c41": "Folder docelowy",
+ "legacy.surface.e8b4d99f2d672a8e": "Niestandardowe profile WebUI",
"legacy.surface.e8e470943fd0f0b6": "Zagnieżdżona kolejność folderów",
"legacy.surface.e948f182d485afee": "Rozwiń oddział",
"legacy.surface.e94d3ee06ecf6aac": "Eksport nie powiódł się",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/docker.json
index 0d4bb07b..8dc31362 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Mascarar IPs públicos",
"docker.privacy.mask-network-interfaces": "Mascarar interfaces de rede",
"docker.privacy.mask-external-urls": "Mascarar URLs externas",
- "docker.actions.capture-diagnostics": "Diagnóstico da página de captura"
+ "docker.actions.capture-diagnostics": "Diagnóstico da página de captura",
+ "docker.folder.hide": "Ocultar pasta",
+ "docker.folder.restore": "Restaurar pasta",
+ "docker.folder.restore-hidden-branch": "Restaurar branch oculto",
+ "docker.folder.hidden-indicator": "Oculto",
+ "docker.folder.unnamed": "Pasta",
+ "docker.folder.visibility-failed": "A preferência de visibilidade da pasta não pôde ser salva.",
+ "docker.visibility.hidden-folders": "Pastas ocultas",
+ "docker.visibility.restore-all": "Restaure todas as pastas ocultas",
+ "docker.visibility.hidden-title": "Pasta oculta",
+ "docker.visibility.folder-fallback": "Pasta",
+ "docker.visibility.hidden-message": "estava oculto da visualização do Docker.",
+ "docker.visibility.undo": "Desfazer",
+ "docker.visibility.all-hidden-title": "Todas as linhas da pasta estão ocultas.",
+ "docker.visibility.all-hidden-help": "Revele-os temporariamente ou restaure todas as pastas ocultas.",
+ "docker.visibility.show-hidden": "Mostrar pastas ocultas",
+ "docker.webui-profiles.open": "Abra o perfil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 de $3 pronto)",
+ "docker.webui-profiles.manage": "Gerenciar perfis WebUI",
+ "docker.webui-profiles.none-title": "Nenhuma WebUIs pronta",
+ "docker.webui-profiles.none-body": "Nenhum dos contêineres selecionados está em execução com uma WebUI disponível."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/editor.json
index 55fbe8cc..e1f03de6 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Compor projeto é igual",
"editor.rules.templates.compose-project-placeholder": "Nome do projeto (exemplo: mídia)",
"editor.rules.templates.label-equals": "Rótulo Docker é igual",
- "editor.rules.templates.label-equals-placeholder": "Chave do rótulo = valor"
+ "editor.rules.templates.label-equals-placeholder": "Chave do rótulo = valor",
+ "editor.webui-profiles.title": "Perfis WebUI personalizados",
+ "editor.webui-profiles.help": "Crie grupos de inicialização reutilizáveis a partir dos membros diretos do Docker desta pasta. Os endereços WebUI atuais e o estado de execução são verificados somente quando um perfil é aberto.",
+ "editor.webui-profiles.add": "Adicionar perfil",
+ "editor.webui-profiles.count": "$1 perfis salvos",
+ "editor.webui-profiles.empty": "Ainda não há perfis WebUI personalizados.",
+ "editor.webui-profiles.name": "Nome do perfil",
+ "editor.webui-profiles.duplicate": "Duplicar",
+ "editor.webui-profiles.move-up": "Subir",
+ "editor.webui-profiles.move-down": "Mover para baixo",
+ "editor.webui-profiles.delete": "Excluir",
+ "editor.webui-profiles.search": "Pesquisar membros da pasta",
+ "editor.webui-profiles.select-webui": "Selecione tudo com WebUI",
+ "editor.webui-profiles.clear": "Limpar seleção",
+ "editor.webui-profiles.webui-ready": "WebUI configurada",
+ "editor.webui-profiles.webui-runtime": "WebUI verificado no lançamento",
+ "editor.webui-profiles.unavailable": "Indisponível nesta pasta",
+ "editor.webui-profiles.no-members": "Nenhum membro da pasta correspondente está disponível.",
+ "editor.webui-profiles.error-name": "Todo perfil WebUI precisa de um nome.",
+ "editor.webui-profiles.error-duplicate": "Os nomes dos perfis WebUI devem ser exclusivos nesta pasta.",
+ "editor.webui-profiles.error-members": "Cada perfil WebUI deve selecionar pelo menos um membro da pasta.",
+ "editor.webui-profiles.limit": "Esta pasta atingiu o limite de segurança do perfil WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/legacy-surface.json
index 4362efd6..64efa467 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-BR/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nenhuma alteração no aplicativo",
"legacy.surface.26f0360c9dddc4d1": "Predefinição excluída",
"legacy.surface.270bca2990500a60": "Escolha onde esta pasta fica na hierarquia.",
+ "legacy.surface.273c468809bda389": "Defina as cores dos indicadores e a cor do texto de status independente usada pelas linhas da pasta.",
"legacy.surface.273f838c039ee10f": "O FolderView Plus detectou que a página Configurações ficou em branco antes da inicialização ser concluída.",
"legacy.surface.2753807153755a5e": "Mesclar (adicionar + atualizar)",
"legacy.surface.2847d4f584d08e0c": "Carregando a visualização de comando do Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Iniciar ramificação ($1)",
"legacy.surface.32c919087b37ec2d": "Pilha de mídia",
"legacy.surface.334277e71d658735": "Depois do alvo",
+ "legacy.surface.33541731c2a77842": "Defina as cores dos indicadores Iniciado, Pausado e Parado independentemente da cor do texto de status nas visualizações Docker, VMs e Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Título das configurações do plug-in",
"legacy.surface.33891d013966c572": "Selecione uma pasta para atribuição em massa.",
"legacy.surface.33c5850b76f401e8": "Pasta de nível superior",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Escolha a mudança de estado e quais membros ela controla.",
"legacy.surface.613aa3b591bacd9a": "Total de armazenamento (discos com suporte de arquivo): $1",
"legacy.surface.61c56bacd53c2dfa": "Cores da divisa:",
+ "legacy.surface.61ea108a3718792d": "Crie grupos nomeados que abrem apenas WebUIs de contêineres Docker selecionados.",
"legacy.surface.61f565ba05bd647d": "Os nomes de contêiner ou VM correspondentes serão incluídos automaticamente aqui.",
"legacy.surface.61fcdf8c82cb687a": "Aplicar tarefas visualizadas?",
"legacy.surface.6233afa7701a7fb8": "Incluir mostrado",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Mexa-se",
"legacy.surface.7179ea0035fca783": "Limpar filtros",
"legacy.surface.7188848298a0b500": "Escopo: favoritos",
+ "legacy.surface.71988c4d8e0803ba": "Texto",
"legacy.surface.719e8555bf1abeba": "Use a etapa Modelos, importe arquivos ou crie pastas manualmente primeiro.",
"legacy.surface.71b2e8d085cdca1e": "Pronto: todas as verificações foram aprovadas.",
"legacy.surface.7205aff54d71cb8b": "Copie primeiro as configurações de pasta de outra pasta do Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Visualização:",
"legacy.surface.75bf7d9afc988cb6": "As pastas são classificadas por nome de A a Z. As pastas aninhadas ainda permanecem dentro de sua ramificação pai.",
"legacy.surface.75d599007fcf49af": "Todos os membros",
- "legacy.surface.75d7a33335c4f033": "Defina as cores iniciadas, pausadas e interrompidas usadas pelas linhas da pasta.",
"legacy.surface.7611fe57a952da4b": "Recomendado. Bloqueia alterações arriscadas automaticamente.",
"legacy.surface.764210a29b968296": "Categoria de modelo",
"legacy.surface.764b74dea7f342ff": "Controla quantos níveis de pastas aninhadas aparecem como chips quando as pastas filhas são mostradas na visualização recolhida.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Corpo da tabela VM",
"legacy.surface.8569ce7917f23eb6": "Excluir $1 regra(s) selecionada(s)?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "Perfis WebUI",
"legacy.surface.85c83f4500bb9f2c": "Informações de tempo de execução da VM",
"legacy.surface.85e2efe37bad1a44": "Nenhum item qualificado para esta ação.",
"legacy.surface.8600edb883562f2c": "Mascarar IPs de LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Analise a configuração atual do Docker e da pasta VM.",
"legacy.surface.916b423aa46d7493": "Foram detectados problemas de aconselhamento de $1 (profundidade/ramificação vazia), mas nenhum erro de link órfão/de ciclo para reparo automático.",
"legacy.surface.91f41a1797b442ab": "Atualizado $1 do modelo salvo.",
- "legacy.surface.920b3e3ce5710d1d": "Defina cores personalizadas para o ícone de status e o texto desta pasta nas visualizações Docker, VMs e Dashboard.",
"legacy.surface.920e413c7d411b61": "Estado",
"legacy.surface.9213c7f4046c085d": "Limite de alerta para atenção",
"legacy.surface.921b15089caf4ff8": "3. Revise a ordem das regras",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controla como as atualizações pendentes afetam a integridade desta pasta.",
"legacy.surface.926e4eab47bf42ea": "Correspondências ao vivo: insira um padrão regex.",
"legacy.surface.92c1cdfdf4cb9cf6": "Habilitado",
+ "legacy.surface.92cff14b4b7992dd": "Selecione os membros diretos da pasta abertos por cada perfil nomeado.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Nenhum ícone personalizado encontrado. Faça upload de um ícone para começar.",
"legacy.surface.93b1554a829a9433": "3 níveis",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Alterações não salvas",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normais",
- "legacy.surface.a759d24fa1d32402": "Abra todas as WebUIs",
"legacy.surface.a75ee6a28c39f865": "Arraste as pastas secundárias para controlar a ordem mostrada nas visualizações e nas linhas expandidas.",
"legacy.surface.a76ac50faa110d7d": "Pastas criadas mais antigas primeiro",
"legacy.surface.a76e13b9839270eb": "Restaurar",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Teste a seco",
"legacy.surface.d640c7421da06661": "Currículo",
"legacy.surface.d66d27cf2aa666c8": "Opções de exibição",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Perfil",
"legacy.surface.d69b03bf0620f859": "Traga uma configuração existente do FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Bloquear pop-ups",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Comparar",
"legacy.surface.e89b30aa1dc30a6a": "Recursos",
"legacy.surface.e89c3109e1f14c41": "Pasta de destino",
+ "legacy.surface.e8b4d99f2d672a8e": "Perfis WebUI personalizados",
"legacy.surface.e8e470943fd0f0b6": "Ordem de pastas aninhadas",
"legacy.surface.e948f182d485afee": "Expandir filial",
"legacy.surface.e94d3ee06ecf6aac": "Falha na exportação",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/docker.json
index 1e3e79d4..0968e540 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Mascarar IPs públicos",
"docker.privacy.mask-network-interfaces": "Mascarar interfaces de rede",
"docker.privacy.mask-external-urls": "Mascarar URLs externas",
- "docker.actions.capture-diagnostics": "Diagnóstico da página de captura"
+ "docker.actions.capture-diagnostics": "Diagnóstico da página de captura",
+ "docker.folder.hide": "Ocultar pasta",
+ "docker.folder.restore": "Restaurar pasta",
+ "docker.folder.restore-hidden-branch": "Restaurar branch oculto",
+ "docker.folder.hidden-indicator": "Oculto",
+ "docker.folder.unnamed": "Pasta",
+ "docker.folder.visibility-failed": "A preferência de visibilidade da pasta não pôde ser salva.",
+ "docker.visibility.hidden-folders": "Pastas ocultas",
+ "docker.visibility.restore-all": "Restaure todas as pastas ocultas",
+ "docker.visibility.hidden-title": "Pasta oculta",
+ "docker.visibility.folder-fallback": "Pasta",
+ "docker.visibility.hidden-message": "estava oculto da visualização do Docker.",
+ "docker.visibility.undo": "Desfazer",
+ "docker.visibility.all-hidden-title": "Todas as linhas da pasta estão ocultas.",
+ "docker.visibility.all-hidden-help": "Revele-os temporariamente ou restaure todas as pastas ocultas.",
+ "docker.visibility.show-hidden": "Mostrar pastas ocultas",
+ "docker.webui-profiles.open": "Abra o perfil WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 de $3 pronto)",
+ "docker.webui-profiles.manage": "Gerenciar perfis WebUI",
+ "docker.webui-profiles.none-title": "Nenhuma WebUIs pronta",
+ "docker.webui-profiles.none-body": "Nenhum dos contêineres selecionados está em execução com uma WebUI disponível."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/editor.json
index da979467..68600c2a 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Compor projeto é igual",
"editor.rules.templates.compose-project-placeholder": "Nome do projeto (exemplo: mídia)",
"editor.rules.templates.label-equals": "Rótulo Docker é igual",
- "editor.rules.templates.label-equals-placeholder": "Chave do rótulo = valor"
+ "editor.rules.templates.label-equals-placeholder": "Chave do rótulo = valor",
+ "editor.webui-profiles.title": "Perfis WebUI personalizados",
+ "editor.webui-profiles.help": "Crie grupos de inicialização reutilizáveis a partir dos membros diretos do Docker desta pasta. Os endereços WebUI atuais e o estado de execução são verificados somente quando um perfil é aberto.",
+ "editor.webui-profiles.add": "Adicionar perfil",
+ "editor.webui-profiles.count": "$1 perfis salvos",
+ "editor.webui-profiles.empty": "Ainda não há perfis WebUI personalizados.",
+ "editor.webui-profiles.name": "Nome do perfil",
+ "editor.webui-profiles.duplicate": "Duplicar",
+ "editor.webui-profiles.move-up": "Subir",
+ "editor.webui-profiles.move-down": "Mover para baixo",
+ "editor.webui-profiles.delete": "Excluir",
+ "editor.webui-profiles.search": "Pesquisar membros da pasta",
+ "editor.webui-profiles.select-webui": "Selecione tudo com WebUI",
+ "editor.webui-profiles.clear": "Limpar seleção",
+ "editor.webui-profiles.webui-ready": "WebUI configurada",
+ "editor.webui-profiles.webui-runtime": "WebUI verificado no lançamento",
+ "editor.webui-profiles.unavailable": "Indisponível nesta pasta",
+ "editor.webui-profiles.no-members": "Nenhum membro da pasta correspondente está disponível.",
+ "editor.webui-profiles.error-name": "Todo perfil WebUI precisa de um nome.",
+ "editor.webui-profiles.error-duplicate": "Os nomes dos perfis WebUI devem ser exclusivos nesta pasta.",
+ "editor.webui-profiles.error-members": "Cada perfil WebUI deve selecionar pelo menos um membro da pasta.",
+ "editor.webui-profiles.limit": "Esta pasta atingiu o limite de segurança do perfil WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/legacy-surface.json
index 6d5bddbb..a4fe59c3 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/pt-PT/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nenhuma alteração no aplicativo",
"legacy.surface.26f0360c9dddc4d1": "Predefinição excluída",
"legacy.surface.270bca2990500a60": "Escolha onde esta pasta fica na hierarquia.",
+ "legacy.surface.273c468809bda389": "Defina as cores dos indicadores e a cor do texto de status independente usada pelas linhas da pasta.",
"legacy.surface.273f838c039ee10f": "O FolderView Plus detectou que a página Configurações ficou em branco antes da inicialização ser concluída.",
"legacy.surface.2753807153755a5e": "Mesclar (adicionar + atualizar)",
"legacy.surface.2847d4f584d08e0c": "Carregando a visualização de comando do Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Iniciar ramificação ($1)",
"legacy.surface.32c919087b37ec2d": "Pilha de mídia",
"legacy.surface.334277e71d658735": "Depois do alvo",
+ "legacy.surface.33541731c2a77842": "Defina as cores dos indicadores Iniciado, Pausado e Parado independentemente da cor do texto de status nas visualizações Docker, VMs e Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Título das configurações do plug-in",
"legacy.surface.33891d013966c572": "Selecione uma pasta para atribuição em massa.",
"legacy.surface.33c5850b76f401e8": "Pasta de nível superior",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Escolha a mudança de estado e quais membros ela controla.",
"legacy.surface.613aa3b591bacd9a": "Total de armazenamento (discos com suporte de arquivo): $1",
"legacy.surface.61c56bacd53c2dfa": "Cores da divisa:",
+ "legacy.surface.61ea108a3718792d": "Crie grupos nomeados que abrem apenas WebUIs de contêineres Docker selecionados.",
"legacy.surface.61f565ba05bd647d": "Os nomes de contêiner ou VM correspondentes serão incluídos automaticamente aqui.",
"legacy.surface.61fcdf8c82cb687a": "Aplicar tarefas visualizadas?",
"legacy.surface.6233afa7701a7fb8": "Incluir mostrado",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Mexa-se",
"legacy.surface.7179ea0035fca783": "Limpar filtros",
"legacy.surface.7188848298a0b500": "Escopo: favoritos",
+ "legacy.surface.71988c4d8e0803ba": "Texto",
"legacy.surface.719e8555bf1abeba": "Use a etapa Modelos, importe arquivos ou crie pastas manualmente primeiro.",
"legacy.surface.71b2e8d085cdca1e": "Pronto: todas as verificações foram aprovadas.",
"legacy.surface.7205aff54d71cb8b": "Copie primeiro as configurações de pasta de outra pasta do Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Visualização:",
"legacy.surface.75bf7d9afc988cb6": "As pastas são classificadas por nome de A a Z. As pastas aninhadas ainda permanecem dentro de sua ramificação pai.",
"legacy.surface.75d599007fcf49af": "Todos os membros",
- "legacy.surface.75d7a33335c4f033": "Defina as cores iniciadas, pausadas e interrompidas usadas pelas linhas da pasta.",
"legacy.surface.7611fe57a952da4b": "Recomendado. Bloqueia alterações arriscadas automaticamente.",
"legacy.surface.764210a29b968296": "Categoria de modelo",
"legacy.surface.764b74dea7f342ff": "Controla quantos níveis de pastas aninhadas aparecem como chips quando as pastas filhas são mostradas na visualização recolhida.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Corpo da tabela VM",
"legacy.surface.8569ce7917f23eb6": "Excluir $1 regra(s) selecionada(s)?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "Perfis WebUI",
"legacy.surface.85c83f4500bb9f2c": "Informações de tempo de execução da VM",
"legacy.surface.85e2efe37bad1a44": "Nenhum item qualificado para esta ação.",
"legacy.surface.8600edb883562f2c": "Mascarar IPs de LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Analise a configuração atual do Docker e da pasta VM.",
"legacy.surface.916b423aa46d7493": "Foram detectados problemas de aconselhamento de $1 (profundidade/ramificação vazia), mas nenhum erro de link órfão/de ciclo para reparo automático.",
"legacy.surface.91f41a1797b442ab": "Atualizado $1 do modelo salvo.",
- "legacy.surface.920b3e3ce5710d1d": "Defina cores personalizadas para o ícone de status e o texto desta pasta nas visualizações Docker, VMs e Dashboard.",
"legacy.surface.920e413c7d411b61": "Estado",
"legacy.surface.9213c7f4046c085d": "Limite de alerta para atenção",
"legacy.surface.921b15089caf4ff8": "3. Revise a ordem das regras",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controla como as atualizações pendentes afetam a integridade desta pasta.",
"legacy.surface.926e4eab47bf42ea": "Correspondências ao vivo: insira um padrão regex.",
"legacy.surface.92c1cdfdf4cb9cf6": "Habilitado",
+ "legacy.surface.92cff14b4b7992dd": "Selecione os membros diretos da pasta abertos por cada perfil nomeado.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Nenhum ícone personalizado encontrado. Faça upload de um ícone para começar.",
"legacy.surface.93b1554a829a9433": "3 níveis",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Alterações não salvas",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normais",
- "legacy.surface.a759d24fa1d32402": "Abra todas as WebUIs",
"legacy.surface.a75ee6a28c39f865": "Arraste as pastas secundárias para controlar a ordem mostrada nas visualizações e nas linhas expandidas.",
"legacy.surface.a76ac50faa110d7d": "Pastas criadas mais antigas primeiro",
"legacy.surface.a76e13b9839270eb": "Restaurar",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Teste a seco",
"legacy.surface.d640c7421da06661": "Currículo",
"legacy.surface.d66d27cf2aa666c8": "Opções de exibição",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Perfil",
"legacy.surface.d69b03bf0620f859": "Traga uma configuração existente do FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Bloquear pop-ups",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Comparar",
"legacy.surface.e89b30aa1dc30a6a": "Recursos",
"legacy.surface.e89c3109e1f14c41": "Pasta de destino",
+ "legacy.surface.e8b4d99f2d672a8e": "Perfis WebUI personalizados",
"legacy.surface.e8e470943fd0f0b6": "Ordem de pastas aninhadas",
"legacy.surface.e948f182d485afee": "Expandir filial",
"legacy.surface.e94d3ee06ecf6aac": "Falha na exportação",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/docker.json
index a989e7e0..2f225f01 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Maschează IPs publice",
"docker.privacy.mask-network-interfaces": "Maschează interfețele de rețea",
"docker.privacy.mask-external-urls": "Maschează URL-urile externe",
- "docker.actions.capture-diagnostics": "Capturați diagnosticarea paginii"
+ "docker.actions.capture-diagnostics": "Capturați diagnosticarea paginii",
+ "docker.folder.hide": "Ascunde folderul",
+ "docker.folder.restore": "Restaurați folderul",
+ "docker.folder.restore-hidden-branch": "Restaurați ramura ascunsă",
+ "docker.folder.hidden-indicator": "Ascuns",
+ "docker.folder.unnamed": "Dosar",
+ "docker.folder.visibility-failed": "Preferința de vizibilitate a folderului nu a putut fi salvată.",
+ "docker.visibility.hidden-folders": "Dosare ascunse",
+ "docker.visibility.restore-all": "Restaurați toate folderele ascunse",
+ "docker.visibility.hidden-title": "Folder ascuns",
+ "docker.visibility.folder-fallback": "Dosar",
+ "docker.visibility.hidden-message": "a fost ascuns din vizualizarea Docker.",
+ "docker.visibility.undo": "Anulați",
+ "docker.visibility.all-hidden-title": "Toate rândurile de foldere sunt ascunse.",
+ "docker.visibility.all-hidden-help": "Dezvăluiți-le temporar sau restaurați fiecare folder ascuns.",
+ "docker.visibility.show-hidden": "Afișează folderele ascunse",
+ "docker.webui-profiles.open": "Deschideți profilul WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 din $3 gata)",
+ "docker.webui-profiles.manage": "Gestionați profilurile WebUI",
+ "docker.webui-profiles.none-title": "Nicio interfață web gata",
+ "docker.webui-profiles.none-body": "Niciunul dintre containerele selectate nu rulează cu un WebUI disponibil."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/editor.json
index 8eb02ee7..58a86103 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Compune proiect egal",
"editor.rules.templates.compose-project-placeholder": "Numele proiectului (exemplu: media)",
"editor.rules.templates.label-equals": "Eticheta Docker este egală",
- "editor.rules.templates.label-equals-placeholder": "Etichetă cheie=valoare"
+ "editor.rules.templates.label-equals-placeholder": "Etichetă cheie=valoare",
+ "editor.webui-profiles.title": "Profiluri WebUI personalizate",
+ "editor.webui-profiles.help": "Creați grupuri de lansare reutilizabile din membrii Docker direcți ai acestui folder. Adresele WebUI curente și starea de rulare sunt verificate numai atunci când este deschis un profil.",
+ "editor.webui-profiles.add": "Adăugați profil",
+ "editor.webui-profiles.count": "$1 profil(e) salvat(e)",
+ "editor.webui-profiles.empty": "Încă nu există profiluri personalizate WebUI.",
+ "editor.webui-profiles.name": "Numele profilului",
+ "editor.webui-profiles.duplicate": "Duplicat",
+ "editor.webui-profiles.move-up": "Mișcă-te în sus",
+ "editor.webui-profiles.move-down": "Deplasați-vă în jos",
+ "editor.webui-profiles.delete": "Ștergeți",
+ "editor.webui-profiles.search": "Căutați membrii folderului",
+ "editor.webui-profiles.select-webui": "Selectați-le pe toate cu WebUI",
+ "editor.webui-profiles.clear": "Ștergeți selecția",
+ "editor.webui-profiles.webui-ready": "WebUI configurat",
+ "editor.webui-profiles.webui-runtime": "WebUI verificat la lansare",
+ "editor.webui-profiles.unavailable": "Indisponibil în acest dosar",
+ "editor.webui-profiles.no-members": "Nu sunt disponibili membrii de folder care se potrivesc.",
+ "editor.webui-profiles.error-name": "Fiecare profil WebUI are nevoie de un nume.",
+ "editor.webui-profiles.error-duplicate": "Numele profilului WebUI trebuie să fie unice în acest folder.",
+ "editor.webui-profiles.error-members": "Fiecare profil WebUI trebuie să selecteze cel puțin un membru al folderului.",
+ "editor.webui-profiles.limit": "Acest folder a atins limita de siguranță a profilului WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/legacy-surface.json
index 4f0c1a98..b2c1d8d0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ro/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Nicio modificare a aplicației",
"legacy.surface.26f0360c9dddc4d1": "Presetarea a fost ștearsă",
"legacy.surface.270bca2990500a60": "Alegeți unde se află acest folder în ierarhie.",
+ "legacy.surface.273c468809bda389": "Setați culorile indicatorului și culoarea independentă a textului de stare utilizată de rândurile de foldere.",
"legacy.surface.273f838c039ee10f": "FolderView Plus a detectat că pagina Setări a devenit goală înainte de finalizarea inițializării.",
"legacy.surface.2753807153755a5e": "Îmbinare (adăugați + actualizați)",
"legacy.surface.2847d4f584d08e0c": "Se încarcă vizualizarea comenzii Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Începeți sucursala ($1)",
"legacy.surface.32c919087b37ec2d": "Stiva media",
"legacy.surface.334277e71d658735": "După țintă",
+ "legacy.surface.33541731c2a77842": "Setați culorile indicatorului Pornit, Întrerupt și Oprit independent de culoarea textului de stare în vizualizările Docker, VM și Dashboard.",
"legacy.surface.3388c6d7238cdde6": "Titlul setărilor pluginului",
"legacy.surface.33891d013966c572": "Selectați un dosar pentru atribuirea în bloc.",
"legacy.surface.33c5850b76f401e8": "Dosar de nivel superior",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Alegeți schimbarea de stat și ce membri controlează.",
"legacy.surface.613aa3b591bacd9a": "Stocare totală (discuri susținute de fișiere): $1",
"legacy.surface.61c56bacd53c2dfa": "Culori chevron:",
+ "legacy.surface.61ea108a3718792d": "Creați grupuri denumite care deschid doar interfețele WebUI ale containerului Docker selectate.",
"legacy.surface.61f565ba05bd647d": "Numele de containere sau de VM care se potrivesc vor fi incluse automat aici.",
"legacy.surface.61fcdf8c82cb687a": "Aplicați sarcinile previzualizate?",
"legacy.surface.6233afa7701a7fb8": "Includeți afișat",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Se mișcă",
"legacy.surface.7179ea0035fca783": "Ștergeți filtrele",
"legacy.surface.7188848298a0b500": "Domeniu de aplicare: favorite",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.719e8555bf1abeba": "Utilizați mai întâi pasul Șabloane, importați fișiere sau creați foldere manual.",
"legacy.surface.71b2e8d085cdca1e": "Gata: toate verificările au trecut.",
"legacy.surface.7205aff54d71cb8b": "Copiați mai întâi setările folderului dintr-un alt folder Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Previzualizare:",
"legacy.surface.75bf7d9afc988cb6": "Dosarele sunt sortate după nume de la A la Z. Dosarele imbricate rămân în continuare în ramura lor părinte.",
"legacy.surface.75d599007fcf49af": "Toată calitatea de membru",
- "legacy.surface.75d7a33335c4f033": "Setați culorile începute, întrerupte și oprite utilizate de rândurile de foldere.",
"legacy.surface.7611fe57a952da4b": "Recomandat. Blochează automat modificările riscante.",
"legacy.surface.764210a29b968296": "Categorie șabloane",
"legacy.surface.764b74dea7f342ff": "Controlează câte niveluri de foldere imbricate apar ca cipuri atunci când folderele secundare sunt afișate în previzualizarea restrânsă.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Corpul tabelului VM",
"legacy.surface.8569ce7917f23eb6": "Ștergeți regulile selectate de $1?",
"legacy.surface.858e4ba7a29fd38b": "Pauză",
+ "legacy.surface.85b6c17862a72780": "Profiluri WebUI",
"legacy.surface.85c83f4500bb9f2c": "Informații despre runtime VM",
"legacy.surface.85e2efe37bad1a44": "Nu există articole eligibile pentru această acțiune.",
"legacy.surface.8600edb883562f2c": "Mascați IP-urile LAN",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Scanați configurația actuală a folderului Docker și VM.",
"legacy.surface.916b423aa46d7493": "S-au detectat $1 probleme de consiliere (adâncime/ramură goală), dar nicio eroare de legătură orfană/ciclică pentru repararea automată.",
"legacy.surface.91f41a1797b442ab": "S-a actualizat $1 din șablonul salvat.",
- "legacy.surface.920b3e3ce5710d1d": "Setați culori personalizate pentru pictograma de stare și textul acestui dosar în vizualizările Docker, VM-uri și tablou de bord.",
"legacy.surface.920e413c7d411b61": "Stare",
"legacy.surface.9213c7f4046c085d": "Avertizați pragul pentru atenție",
"legacy.surface.921b15089caf4ff8": "3. Verificați ordinea regulilor",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Controlează modul în care actualizările în așteptare afectează sănătatea acestui folder.",
"legacy.surface.926e4eab47bf42ea": "Potriviri live: introduceți un model regex.",
"legacy.surface.92c1cdfdf4cb9cf6": "Activat",
+ "legacy.surface.92cff14b4b7992dd": "Selectați membrii directoare a folderului deschis de fiecare profil numit.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Nu s-au găsit pictograme personalizate. Încărcați o pictogramă pentru a începe.",
"legacy.surface.93b1554a829a9433": "3 niveluri",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Modificări nesalvate",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Deschideți toate WebUI-urile",
"legacy.surface.a75ee6a28c39f865": "Trageți dosarele secundare pentru a controla ordinea afișată în previzualizări și rânduri extinse.",
"legacy.surface.a76ac50faa110d7d": "Cele mai vechi foldere create mai întâi",
"legacy.surface.a76e13b9839270eb": "Restaurați",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Cursa uscata",
"legacy.surface.d640c7421da06661": "Reluați",
"legacy.surface.d66d27cf2aa666c8": "Afișează opțiuni",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Introduceți o configurație FolderView existentă.",
"legacy.surface.d6a226a96bdc60fe": "Blocați ferestrele pop-up",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Comparați",
"legacy.surface.e89b30aa1dc30a6a": "Resurse",
"legacy.surface.e89c3109e1f14c41": "Dosarul țintă",
+ "legacy.surface.e8b4d99f2d672a8e": "Profiluri WebUI personalizate",
"legacy.surface.e8e470943fd0f0b6": "Ordinea folderelor imbricate",
"legacy.surface.e948f182d485afee": "Extindeți ramura",
"legacy.surface.e94d3ee06ecf6aac": "Exportul nu a reușit",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/docker.json
index 7fe2cc4d..9f7625e8 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Скрыть публичные IPs",
"docker.privacy.mask-network-interfaces": "Скрыть сетевые интерфейсы",
"docker.privacy.mask-external-urls": "Маскировать внешние URL",
- "docker.actions.capture-diagnostics": "Диагностика страницы захвата"
+ "docker.actions.capture-diagnostics": "Диагностика страницы захвата",
+ "docker.folder.hide": "Скрыть папку",
+ "docker.folder.restore": "Восстановить папку",
+ "docker.folder.restore-hidden-branch": "Восстановить скрытую ветку",
+ "docker.folder.hidden-indicator": "Скрытый",
+ "docker.folder.unnamed": "Папка",
+ "docker.folder.visibility-failed": "Не удалось сохранить настройки видимости папки.",
+ "docker.visibility.hidden-folders": "Скрытые папки",
+ "docker.visibility.restore-all": "Восстановить все скрытые папки",
+ "docker.visibility.hidden-title": "Папка скрыта",
+ "docker.visibility.folder-fallback": "Папка",
+ "docker.visibility.hidden-message": "был скрыт из представления Docker.",
+ "docker.visibility.undo": "Отменить",
+ "docker.visibility.all-hidden-title": "Все строки папки скрыты.",
+ "docker.visibility.all-hidden-help": "Временно покажите их или восстановите все скрытые папки.",
+ "docker.visibility.show-hidden": "Показать скрытые папки",
+ "docker.webui-profiles.open": "Открыть профиль веб-интерфейса",
+ "docker.webui-profiles.ready": "$1 ($2 из $3 готов)",
+ "docker.webui-profiles.manage": "Управление профилями веб-интерфейса",
+ "docker.webui-profiles.none-title": "Нет готовых веб-интерфейсов",
+ "docker.webui-profiles.none-body": "Ни один из выбранных контейнеров не работает с доступным веб-интерфейсом."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/editor.json
index 01b70923..b2a2be8b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Составить проект равен",
"editor.rules.templates.compose-project-placeholder": "Название проекта (пример: СМИ)",
"editor.rules.templates.label-equals": "Метка Docker равна",
- "editor.rules.templates.label-equals-placeholder": "Ключ метки = значение"
+ "editor.rules.templates.label-equals-placeholder": "Ключ метки = значение",
+ "editor.webui-profiles.title": "Пользовательские профили веб-интерфейса",
+ "editor.webui-profiles.help": "Создайте многоразовые группы запуска из прямых участников Docker этой папки. Текущие адреса WebUI и состояние работы проверяются только при открытии профиля.",
+ "editor.webui-profiles.add": "Добавить профиль",
+ "editor.webui-profiles.count": "$1 сохраненных профилей",
+ "editor.webui-profiles.empty": "Настраиваемых профилей WebUI пока нет.",
+ "editor.webui-profiles.name": "Имя профиля",
+ "editor.webui-profiles.duplicate": "Дублировать",
+ "editor.webui-profiles.move-up": "Вверх",
+ "editor.webui-profiles.move-down": "Двигаться вниз",
+ "editor.webui-profiles.delete": "Удалить",
+ "editor.webui-profiles.search": "Поиск участников папки",
+ "editor.webui-profiles.select-webui": "Выбрать все с помощью WebUI",
+ "editor.webui-profiles.clear": "Очистить выбор",
+ "editor.webui-profiles.webui-ready": "Веб-интерфейс настроен",
+ "editor.webui-profiles.webui-runtime": "WebUI проверен при запуске",
+ "editor.webui-profiles.unavailable": "Недоступно в этой папке",
+ "editor.webui-profiles.no-members": "Нет соответствующих элементов папки.",
+ "editor.webui-profiles.error-name": "Каждому профилю WebUI необходимо имя.",
+ "editor.webui-profiles.error-duplicate": "Имена профилей WebUI должны быть уникальными в этой папке.",
+ "editor.webui-profiles.error-members": "В каждом профиле WebUI необходимо выбрать хотя бы одного члена папки.",
+ "editor.webui-profiles.limit": "В этой папке достигнут предел безопасности профиля WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/legacy-surface.json
index 820cf1bc..f088add4 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/ru/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Никаких изменений в приложении",
"legacy.surface.26f0360c9dddc4d1": "Предустановка удалена.",
"legacy.surface.270bca2990500a60": "Выберите, где в иерархии будет находиться эта папка.",
+ "legacy.surface.273c468809bda389": "Установите цвета индикаторов и независимый цвет текста статуса, используемый строками папок.",
"legacy.surface.273f838c039ee10f": "FolderView Plus обнаружил, что страница настроек стала пустой до завершения инициализации.",
"legacy.surface.2753807153755a5e": "Объединить (добавить + обновить)",
"legacy.surface.2847d4f584d08e0c": "Загрузка представления команд Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Начать ветку ($1)",
"legacy.surface.32c919087b37ec2d": "Медиа-стек",
"legacy.surface.334277e71d658735": "После цели",
+ "legacy.surface.33541731c2a77842": "Установите цвета индикаторов «Начато», «Приостановлено» и «Остановлено» независимо от цвета текста состояния в представлениях Docker, виртуальных машин и панели мониторинга.",
"legacy.surface.3388c6d7238cdde6": "Название настроек плагина",
"legacy.surface.33891d013966c572": "Выберите папку для массового назначения.",
"legacy.surface.33c5850b76f401e8": "Папка верхнего уровня",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Выберите изменение состояния и какие элементы оно контролирует.",
"legacy.surface.613aa3b591bacd9a": "Общий объем хранилища (диски с файловыми базами): $1",
"legacy.surface.61c56bacd53c2dfa": "Цвета шеврона:",
+ "legacy.surface.61ea108a3718792d": "Создавайте именованные группы, которые открывают только выбранные веб-интерфейсы контейнера Docker.",
"legacy.surface.61f565ba05bd647d": "Сюда будут автоматически включены соответствующие имена контейнеров или виртуальных машин.",
"legacy.surface.61fcdf8c82cb687a": "Применить предварительно просмотренные задания?",
"legacy.surface.6233afa7701a7fb8": "Включить показанные",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "покачиваться",
"legacy.surface.7179ea0035fca783": "Очистить фильтры",
"legacy.surface.7188848298a0b500": "Область применения: избранное",
+ "legacy.surface.71988c4d8e0803ba": "Текст",
"legacy.surface.719e8555bf1abeba": "Используйте шаг «Шаблоны», сначала импортируйте файлы или создайте папки вручную.",
"legacy.surface.71b2e8d085cdca1e": "Готово: все проверки пройдены.",
"legacy.surface.7205aff54d71cb8b": "Сначала скопируйте настройки папки из другой папки Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Предварительный просмотр:",
"legacy.surface.75bf7d9afc988cb6": "Папки сортируются по имени от А до Я. Вложенные папки по-прежнему остаются внутри родительской ветки.",
"legacy.surface.75d599007fcf49af": "Все членство",
- "legacy.surface.75d7a33335c4f033": "Установите цвета запуска, паузы и остановки, используемые для строк папки.",
"legacy.surface.7611fe57a952da4b": "Рекомендуется. Автоматически блокирует рискованные изменения.",
"legacy.surface.764210a29b968296": "Категория шаблона",
"legacy.surface.764b74dea7f342ff": "Определяет, сколько уровней вложенных папок отображается в виде фрагментов, когда дочерние папки отображаются в свернутом предварительном просмотре.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Тело таблицы виртуальной машины",
"legacy.surface.8569ce7917f23eb6": "Удалить выбранные правила ($1)?",
"legacy.surface.858e4ba7a29fd38b": "Пауза",
+ "legacy.surface.85b6c17862a72780": "Профили веб-интерфейса",
"legacy.surface.85c83f4500bb9f2c": "Информация о времени выполнения виртуальной машины",
"legacy.surface.85e2efe37bad1a44": "Нет подходящих элементов для этого действия.",
"legacy.surface.8600edb883562f2c": "Маска IP-адресов локальной сети",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Сканируйте текущую настройку папки Docker и виртуальной машины.",
"legacy.surface.916b423aa46d7493": "Обнаружены проблемы с рекомендациями стоимостью $1 (глубина/пустая ветвь), но нет ошибок потерянных ссылок/циклических ссылок для автоматического восстановления.",
"legacy.surface.91f41a1797b442ab": "Обновлен $1 из сохраненного шаблона.",
- "legacy.surface.920b3e3ce5710d1d": "Установите собственные цвета для значка состояния и текста этой папки в представлениях Docker, виртуальных машин и панели мониторинга.",
"legacy.surface.920e413c7d411b61": "Статус",
"legacy.surface.9213c7f4046c085d": "Порог предупреждения для внимания",
"legacy.surface.921b15089caf4ff8": "3. Просмотрите порядок правил.",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Управляет тем, как ожидающие обновления влияют на состояние этой папки.",
"legacy.surface.926e4eab47bf42ea": "Живые совпадения: введите шаблон регулярного выражения.",
"legacy.surface.92c1cdfdf4cb9cf6": "Включено",
+ "legacy.surface.92cff14b4b7992dd": "Выберите элементы прямой папки, открытые каждым именованным профилем.",
"legacy.surface.92ec9d61393d7064": "$1 регулярное выражение",
"legacy.surface.93aa7d6a1b93167d": "Пользовательские значки не найдены. Загрузите значок, чтобы начать.",
"legacy.surface.93b1554a829a9433": "3 уровня",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Несохраненные изменения",
"legacy.surface.a71d508e50432f40": "Домашняя лаборатория",
"legacy.surface.a7248eeb45eb1ce2": "Нормальный",
- "legacy.surface.a759d24fa1d32402": "Открыть все веб-интерфейсы",
"legacy.surface.a75ee6a28c39f865": "Перетаскивайте дочерние папки, чтобы контролировать порядок отображения в предварительных просмотрах и развернутых строках.",
"legacy.surface.a76ac50faa110d7d": "Сначала самые старые созданные папки",
"legacy.surface.a76e13b9839270eb": "Восстановить",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Сухой пробег",
"legacy.surface.d640c7421da06661": "Резюме",
"legacy.surface.d66d27cf2aa666c8": "Варианты отображения",
+ "legacy.surface.d693881b5ac21882": "--fvplus-folder-status-text",
"legacy.surface.d696a35bdd1883da": "Профиль",
"legacy.surface.d69b03bf0620f859": "Добавьте существующую настройку FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Блокировать всплывающие окна",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Сравнить",
"legacy.surface.e89b30aa1dc30a6a": "Ресурсы",
"legacy.surface.e89c3109e1f14c41": "Целевая папка",
+ "legacy.surface.e8b4d99f2d672a8e": "Пользовательские профили веб-интерфейса",
"legacy.surface.e8e470943fd0f0b6": "Порядок вложенных папок",
"legacy.surface.e948f182d485afee": "Развернуть ветку",
"legacy.surface.e94d3ee06ecf6aac": "Экспорт не удался",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/docker.json
index 86393144..7b48c267 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Dölj offentlig IPs",
"docker.privacy.mask-network-interfaces": "Dölj nätverksgränssnitt",
"docker.privacy.mask-external-urls": "Maskera externa URL:er",
- "docker.actions.capture-diagnostics": "Fånga sida diagnostik"
+ "docker.actions.capture-diagnostics": "Fånga sida diagnostik",
+ "docker.folder.hide": "Dölj mapp",
+ "docker.folder.restore": "Återställ mapp",
+ "docker.folder.restore-hidden-branch": "Återställ dold gren",
+ "docker.folder.hidden-indicator": "Dold",
+ "docker.folder.unnamed": "Mapp",
+ "docker.folder.visibility-failed": "Inställningen för mappsynlighet kunde inte sparas.",
+ "docker.visibility.hidden-folders": "Dolda mappar",
+ "docker.visibility.restore-all": "Återställ alla dolda mappar",
+ "docker.visibility.hidden-title": "Mapp dold",
+ "docker.visibility.folder-fallback": "Mapp",
+ "docker.visibility.hidden-message": "var dolt från Docker-vyn.",
+ "docker.visibility.undo": "Ångra",
+ "docker.visibility.all-hidden-title": "Alla mapprader är dolda.",
+ "docker.visibility.all-hidden-help": "Visa dem tillfälligt eller återställ alla dolda mappar.",
+ "docker.visibility.show-hidden": "Visa dolda mappar",
+ "docker.webui-profiles.open": "Open WebUI profile",
+ "docker.webui-profiles.ready": "$1 ($2 av $3 redo)",
+ "docker.webui-profiles.manage": "Manage WebUI profiles",
+ "docker.webui-profiles.none-title": "No WebUIs ready",
+ "docker.webui-profiles.none-body": "Ingen av de valda behållarna körs med ett tillgängligt WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/editor.json
index db6f921e..5fc910c0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Komponera projekt lika",
"editor.rules.templates.compose-project-placeholder": "Projektnamn (exempel: media)",
"editor.rules.templates.label-equals": "Docker-etikett är lika med",
- "editor.rules.templates.label-equals-placeholder": "Etikett nyckel=värde"
+ "editor.rules.templates.label-equals-placeholder": "Etikett nyckel=värde",
+ "editor.webui-profiles.title": "Anpassade WebUI-profiler",
+ "editor.webui-profiles.help": "Skapa återanvändbara startgrupper från den här mappens direkta Docker-medlemmar. Aktuella WebUI-adresser och körstatus kontrolleras endast när en profil öppnas.",
+ "editor.webui-profiles.add": "Lägg till profil",
+ "editor.webui-profiles.count": "$1 sparad(a) profil(er)",
+ "editor.webui-profiles.empty": "Inga anpassade WebUI-profiler ännu.",
+ "editor.webui-profiles.name": "Profilnamn",
+ "editor.webui-profiles.duplicate": "Duplicera",
+ "editor.webui-profiles.move-up": "Flytta uppåt",
+ "editor.webui-profiles.move-down": "Flytta ner",
+ "editor.webui-profiles.delete": "Ta bort",
+ "editor.webui-profiles.search": "Sök mappmedlemmar",
+ "editor.webui-profiles.select-webui": "Välj alla med WebUI",
+ "editor.webui-profiles.clear": "Rensa val",
+ "editor.webui-profiles.webui-ready": "WebUI konfigurerat",
+ "editor.webui-profiles.webui-runtime": "WebUI kontrolleras vid lansering",
+ "editor.webui-profiles.unavailable": "Ej tillgänglig i den här mappen",
+ "editor.webui-profiles.no-members": "Inga matchande mappmedlemmar är tillgängliga.",
+ "editor.webui-profiles.error-name": "Varje WebUI-profil behöver ett namn.",
+ "editor.webui-profiles.error-duplicate": "WebUI-profilnamn måste vara unika i den här mappen.",
+ "editor.webui-profiles.error-members": "Varje WebUI-profil måste välja minst en mappmedlem.",
+ "editor.webui-profiles.limit": "Den här mappen har nått säkerhetsgränsen för WebUI-profilen."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/legacy-surface.json
index 1b156a3b..d9f736de 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/sv/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Inga appändringar",
"legacy.surface.26f0360c9dddc4d1": "Förinställning raderad",
"legacy.surface.270bca2990500a60": "Välj var den här mappen finns i hierarkin.",
+ "legacy.surface.273c468809bda389": "Ställ in indikatorfärger och den oberoende statustextfärgen som används av mapprader.",
"legacy.surface.273f838c039ee10f": "FolderView Plus upptäckte att sidan Inställningar blev tom innan initieringen slutfördes.",
"legacy.surface.2753807153755a5e": "Slå samman (lägg till + uppdatera)",
"legacy.surface.2847d4f584d08e0c": "Laddar Docker-kommandovy...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Starta filial ($1)",
"legacy.surface.32c919087b37ec2d": "Media Stack",
"legacy.surface.334277e71d658735": "Efter mål",
+ "legacy.surface.33541731c2a77842": "Ställ in indikatorfärgerna Startad, Pausad och Stoppad oberoende av statustextfärgen i Docker-, VM- och Dashboard-vyerna.",
"legacy.surface.3388c6d7238cdde6": "Titel för plugininställningar",
"legacy.surface.33891d013966c572": "Välj en mapp för masstilldelning.",
"legacy.surface.33c5850b76f401e8": "Mapp på toppnivå",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Välj tillståndsändringen och vilka medlemmar den kontrollerar.",
"legacy.surface.613aa3b591bacd9a": "Totalt lagringsutrymme (filstödda diskar): $1",
"legacy.surface.61c56bacd53c2dfa": "Chevron färger:",
+ "legacy.surface.61ea108a3718792d": "Skapa namngivna grupper som endast öppnar utvalda Docker-container WebUIs.",
"legacy.surface.61f565ba05bd647d": "Matchande container- eller virtuella datornamn kommer att inkluderas automatiskt här.",
"legacy.surface.61fcdf8c82cb687a": "Vill du använda förhandsgranskade uppgifter?",
"legacy.surface.6233afa7701a7fb8": "Inkludera visat",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Vicka",
"legacy.surface.7179ea0035fca783": "Rensa filter",
"legacy.surface.7188848298a0b500": "Omfattning: favoriter",
+ "legacy.surface.71988c4d8e0803ba": "Text",
"legacy.surface.719e8555bf1abeba": "Använd steget Mallar, importera filer eller skapa mappar manuellt först.",
"legacy.surface.71b2e8d085cdca1e": "Klar: alla kontroller godkända.",
"legacy.surface.7205aff54d71cb8b": "Kopiera mappinställningar från en annan Docker-mapp först.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Förhandsgranska:",
"legacy.surface.75bf7d9afc988cb6": "Mappar sorteras efter namn från A till Ö. Kapslade mappar finns fortfarande kvar i sin överordnade gren.",
"legacy.surface.75d599007fcf49af": "Allt medlemskap",
- "legacy.surface.75d7a33335c4f033": "Ställ in färgerna för startade, pausade och stoppade som används av mapprader.",
"legacy.surface.7611fe57a952da4b": "Rekommenderas. Blockerar riskabla ändringar automatiskt.",
"legacy.surface.764210a29b968296": "Mallkategori",
"legacy.surface.764b74dea7f342ff": "Styr hur många kapslade mappnivåer som visas som marker när underordnade mappar visas i den komprimerade förhandsvisningen.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM bordskropp",
"legacy.surface.8569ce7917f23eb6": "Vill du ta bort $1 valda regler?",
"legacy.surface.858e4ba7a29fd38b": "Pausa",
+ "legacy.surface.85b6c17862a72780": "WebUI Profiles",
"legacy.surface.85c83f4500bb9f2c": "VM runtime info",
"legacy.surface.85e2efe37bad1a44": "Inga kvalificerade objekt för denna åtgärd.",
"legacy.surface.8600edb883562f2c": "Maskera LAN-IP:er",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Skanna din nuvarande Docker- och VM-mappinstallation.",
"legacy.surface.916b423aa46d7493": "Upptäckte $1 rådgivande problem (djup/tom gren), men inga föräldralösa/cykellänksfel för autoreparation.",
"legacy.surface.91f41a1797b442ab": "Uppdaterade $1 från sparad mall.",
- "legacy.surface.920b3e3ce5710d1d": "Ställ in anpassade färger för den här mappens statusikon och text i Docker-, VM- och Dashboard-vyer.",
"legacy.surface.920e413c7d411b61": "Status",
"legacy.surface.9213c7f4046c085d": "Varningströskel för uppmärksamhet",
"legacy.surface.921b15089caf4ff8": "3. Granska regelordningen",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Styr hur väntande uppdateringar påverkar mappens hälsa.",
"legacy.surface.926e4eab47bf42ea": "Livematcher: ange ett regexmönster.",
"legacy.surface.92c1cdfdf4cb9cf6": "Aktiverad",
+ "legacy.surface.92cff14b4b7992dd": "Välj direktmappmedlemmarna som öppnas av varje namngiven profil.",
"legacy.surface.92ec9d61393d7064": "$1 regex",
"legacy.surface.93aa7d6a1b93167d": "Inga anpassade ikoner hittades. Ladda upp en ikon för att komma igång.",
"legacy.surface.93b1554a829a9433": "3 nivåer",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Osparade ändringar",
"legacy.surface.a71d508e50432f40": "Homelab",
"legacy.surface.a7248eeb45eb1ce2": "Normal",
- "legacy.surface.a759d24fa1d32402": "Öppna alla webbgränssnitt",
"legacy.surface.a75ee6a28c39f865": "Dra underordnade mappar för att kontrollera ordningen som visas i förhandsgranskningar och utökade rader.",
"legacy.surface.a76ac50faa110d7d": "Äldsta skapade mappar först",
"legacy.surface.a76e13b9839270eb": "Återställ",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Torrkörning",
"legacy.surface.d640c7421da06661": "Återuppta",
"legacy.surface.d66d27cf2aa666c8": "Visa val",
+ "legacy.surface.d693881b5ac21882": "--fvplus-mapp-status-text",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Ta in en befintlig FolderView-inställning.",
"legacy.surface.d6a226a96bdc60fe": "Blockera popup-fönster",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Jämför",
"legacy.surface.e89b30aa1dc30a6a": "Resurser",
"legacy.surface.e89c3109e1f14c41": "Målmapp",
+ "legacy.surface.e8b4d99f2d672a8e": "Custom WebUI Profiles",
"legacy.surface.e8e470943fd0f0b6": "Kapslad mappordning",
"legacy.surface.e948f182d485afee": "Expandera gren",
"legacy.surface.e94d3ee06ecf6aac": "Exporten misslyckades",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/docker.json
index 86000aee..16a74106 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Genel IPs gizle",
"docker.privacy.mask-network-interfaces": "Ağ arabirimlerini gizle",
"docker.privacy.mask-external-urls": "Harici URL'leri maskele",
- "docker.actions.capture-diagnostics": "Yakalama sayfası teşhisleri"
+ "docker.actions.capture-diagnostics": "Yakalama sayfası teşhisleri",
+ "docker.folder.hide": "Klasörü gizle",
+ "docker.folder.restore": "Klasörü geri yükle",
+ "docker.folder.restore-hidden-branch": "Gizli dalı geri yükle",
+ "docker.folder.hidden-indicator": "Gizli",
+ "docker.folder.unnamed": "Klasör",
+ "docker.folder.visibility-failed": "Klasör görünürlük tercihi kaydedilemedi.",
+ "docker.visibility.hidden-folders": "Gizli klasörler",
+ "docker.visibility.restore-all": "Tüm gizli klasörleri geri yükle",
+ "docker.visibility.hidden-title": "Klasör gizlendi",
+ "docker.visibility.folder-fallback": "Klasör",
+ "docker.visibility.hidden-message": "Docker görünümünden gizlendi.",
+ "docker.visibility.undo": "Geri al",
+ "docker.visibility.all-hidden-title": "Tüm klasör satırları gizlidir.",
+ "docker.visibility.all-hidden-help": "Bunları geçici olarak açığa çıkarın veya tüm gizli klasörleri geri yükleyin.",
+ "docker.visibility.show-hidden": "Gizli klasörleri göster",
+ "docker.webui-profiles.open": "WebUI profilini aç",
+ "docker.webui-profiles.ready": "$1 ($2 / $3 hazır)",
+ "docker.webui-profiles.manage": "WebUI profillerini yönetin",
+ "docker.webui-profiles.none-title": "Hazır WebUI yok",
+ "docker.webui-profiles.none-body": "Seçilen kapsayıcılardan hiçbiri kullanılabilir bir WebUI ile çalışmıyor."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/editor.json
index f2deab7f..ac8d480d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Proje eşittir oluştur",
"editor.rules.templates.compose-project-placeholder": "Proje adı (örnek: medya)",
"editor.rules.templates.label-equals": "Docker etiketi eşittir",
- "editor.rules.templates.label-equals-placeholder": "Etiket anahtarı=değer"
+ "editor.rules.templates.label-equals-placeholder": "Etiket anahtarı=değer",
+ "editor.webui-profiles.title": "Özel WebUI profilleri",
+ "editor.webui-profiles.help": "Bu klasörün doğrudan Docker üyelerinden yeniden kullanılabilir başlatma grupları oluşturun. Geçerli WebUI adresleri ve çalışma durumu yalnızca bir profil açıldığında kontrol edilir.",
+ "editor.webui-profiles.add": "Profil ekle",
+ "editor.webui-profiles.count": "$1 kayıtlı profil(ler)",
+ "editor.webui-profiles.empty": "Henüz özel WebUI profili yok.",
+ "editor.webui-profiles.name": "Profil adı",
+ "editor.webui-profiles.duplicate": "Çoğalt",
+ "editor.webui-profiles.move-up": "Yukarı taşı",
+ "editor.webui-profiles.move-down": "Aşağı taşı",
+ "editor.webui-profiles.delete": "Sil",
+ "editor.webui-profiles.search": "Klasör üyelerini ara",
+ "editor.webui-profiles.select-webui": "WebUI ile tümünü seç",
+ "editor.webui-profiles.clear": "Seçimi temizle",
+ "editor.webui-profiles.webui-ready": "WebUI yapılandırıldı",
+ "editor.webui-profiles.webui-runtime": "WebUI açılışta kontrol edildi",
+ "editor.webui-profiles.unavailable": "Bu klasörde kullanılamıyor",
+ "editor.webui-profiles.no-members": "Eşleşen klasör üyesi yok.",
+ "editor.webui-profiles.error-name": "Her WebUI profilinin bir ada ihtiyacı vardır.",
+ "editor.webui-profiles.error-duplicate": "WebUI profil adları bu klasörde benzersiz olmalıdır.",
+ "editor.webui-profiles.error-members": "Her WebUI profili en az bir klasör üyesi seçmelidir.",
+ "editor.webui-profiles.limit": "Bu klasör WebUI profili güvenlik sınırına ulaştı."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/legacy-surface.json
index 71fabed2..048d603b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/tr/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Uygulama değişikliği yok",
"legacy.surface.26f0360c9dddc4d1": "Ön ayar silindi",
"legacy.surface.270bca2990500a60": "Bu klasörün hiyerarşide nerede bulunacağını seçin.",
+ "legacy.surface.273c468809bda389": "Gösterge renklerini ve klasör satırları tarafından kullanılan bağımsız durum metni rengini ayarlayın.",
"legacy.surface.273f838c039ee10f": "FolderView Plus, başlatma tamamlanmadan önce Ayarlar sayfasının boş hale geldiğini tespit etti.",
"legacy.surface.2753807153755a5e": "Birleştir (ekle + güncelle)",
"legacy.surface.2847d4f584d08e0c": "Docker komut görünümü yükleniyor...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Şubeyi başlat ($1)",
"legacy.surface.32c919087b37ec2d": "Medya Yığını",
"legacy.surface.334277e71d658735": "Hedeften sonra",
+ "legacy.surface.33541731c2a77842": "Docker, VM'ler ve Kontrol Paneli görünümlerinde Başlatıldı, Duraklatıldı ve Durduruldu gösterge renklerini durum metni renginden bağımsız olarak ayarlayın.",
"legacy.surface.3388c6d7238cdde6": "Eklenti ayarları başlığı",
"legacy.surface.33891d013966c572": "Toplu atama için bir klasör seçin.",
"legacy.surface.33c5850b76f401e8": "Üst düzey klasör",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Durum değişikliğini ve hangi üyeleri kontrol ettiğini seçin.",
"legacy.surface.613aa3b591bacd9a": "Toplam depolama alanı (dosya destekli diskler): $1",
"legacy.surface.61c56bacd53c2dfa": "Şerit renkleri:",
+ "legacy.surface.61ea108a3718792d": "Yalnızca seçilen Docker kapsayıcısı WebUI'lerini açan adlandırılmış gruplar oluşturun.",
"legacy.surface.61f565ba05bd647d": "Eşleşen kapsayıcı veya VM adları buraya otomatik olarak eklenecektir.",
"legacy.surface.61fcdf8c82cb687a": "Önizlenen ödevler uygulansın mı?",
"legacy.surface.6233afa7701a7fb8": "Gösterilenleri dahil et",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Kıpırdat",
"legacy.surface.7179ea0035fca783": "Filtreleri temizle",
"legacy.surface.7188848298a0b500": "Kapsam: favoriler",
+ "legacy.surface.71988c4d8e0803ba": "Metin",
"legacy.surface.719e8555bf1abeba": "Önce Şablonlar adımını kullanın, dosyaları içe aktarın veya klasörleri manuel olarak oluşturun.",
"legacy.surface.71b2e8d085cdca1e": "Hazır: tüm kontroller geçildi.",
"legacy.surface.7205aff54d71cb8b": "Önce klasör ayarlarını başka bir Docker klasöründen kopyalayın.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Önizleme:",
"legacy.surface.75bf7d9afc988cb6": "Klasörler A'dan Z'ye ada göre sıralanır. İç içe geçmiş klasörler hâlâ ana dallarının içinde kalır.",
"legacy.surface.75d599007fcf49af": "Tüm üyelikler",
- "legacy.surface.75d7a33335c4f033": "Klasör satırları tarafından kullanılan başlatılan, duraklatılan ve durdurulan renkleri ayarlayın.",
"legacy.surface.7611fe57a952da4b": "Tavsiye edilir. Riskli değişiklikleri otomatik olarak engeller.",
"legacy.surface.764210a29b968296": "Şablon kategorisi",
"legacy.surface.764b74dea7f342ff": "Daraltılmış önizlemede alt klasörler gösterildiğinde, kaç tane iç içe geçmiş klasör düzeyinin çip olarak görüneceğini kontrol eder.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM tablo gövdesi",
"legacy.surface.8569ce7917f23eb6": "Seçilen $1 kural silinsin mi?",
"legacy.surface.858e4ba7a29fd38b": "Duraklat",
+ "legacy.surface.85b6c17862a72780": "WebUI Profilleri",
"legacy.surface.85c83f4500bb9f2c": "VM çalışma zamanı bilgileri",
"legacy.surface.85e2efe37bad1a44": "Bu işlem için uygun öğe yok.",
"legacy.surface.8600edb883562f2c": "LAN IP'lerini maskele",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Mevcut Docker ve VM klasör kurulumunuzu tarayın.",
"legacy.surface.916b423aa46d7493": "$1 tavsiye niteliğinde sorun(lar) algılandı (derinlik/boş dal), ancak otomatik onarım için yetim/döngü bağlantısı hatası yok.",
"legacy.surface.91f41a1797b442ab": "Kaydedilen şablondan $1 güncellendi.",
- "legacy.surface.920b3e3ce5710d1d": "Docker, VM'ler ve Kontrol Paneli görünümlerinde bu klasörün durum simgesi ve metni için özel renkler ayarlayın.",
"legacy.surface.920e413c7d411b61": "Durum",
"legacy.surface.9213c7f4046c085d": "Dikkat eşiğini uyar",
"legacy.surface.921b15089caf4ff8": "3. Kural sırasını inceleyin",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Bekleyen güncellemelerin bu klasörün durumunu nasıl etkileyeceğini kontrol eder.",
"legacy.surface.926e4eab47bf42ea": "Canlı maçlar: Bir normal ifade modeli girin.",
"legacy.surface.92c1cdfdf4cb9cf6": "Etkin",
+ "legacy.surface.92cff14b4b7992dd": "Her adlandırılmış profil tarafından açılan doğrudan klasör üyelerini seçin.",
"legacy.surface.92ec9d61393d7064": "$1 normal ifade",
"legacy.surface.93aa7d6a1b93167d": "Özel simge bulunamadı. Başlamak için bir simge yükleyin.",
"legacy.surface.93b1554a829a9433": "3 seviye",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Kaydedilmemiş değişiklikler",
"legacy.surface.a71d508e50432f40": "Ev Laboratuvarı",
"legacy.surface.a7248eeb45eb1ce2": "normal",
- "legacy.surface.a759d24fa1d32402": "Tüm WebUI'ları aç",
"legacy.surface.a75ee6a28c39f865": "Önizlemelerde ve genişletilmiş satırlarda gösterilen sırayı kontrol etmek için alt klasörleri sürükleyin.",
"legacy.surface.a76ac50faa110d7d": "En eski oluşturulan klasörler ilk sırada yer alır",
"legacy.surface.a76e13b9839270eb": "Geri yükle",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Deneme sürüşü",
"legacy.surface.d640c7421da06661": "Devam et",
"legacy.surface.d66d27cf2aa666c8": "Görüntüleme seçenekleri",
+ "legacy.surface.d693881b5ac21882": "--fvplus-klasör-durum-metni",
"legacy.surface.d696a35bdd1883da": "Profil",
"legacy.surface.d69b03bf0620f859": "Mevcut bir FolderView kurulumunu getirin.",
"legacy.surface.d6a226a96bdc60fe": "Açılır Pencereleri Engelle",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Karşılaştır",
"legacy.surface.e89b30aa1dc30a6a": "Kaynaklar",
"legacy.surface.e89c3109e1f14c41": "Hedef klasör",
+ "legacy.surface.e8b4d99f2d672a8e": "Özel WebUI Profilleri",
"legacy.surface.e8e470943fd0f0b6": "İç içe klasör sırası",
"legacy.surface.e948f182d485afee": "Şubeyi genişlet",
"legacy.surface.e94d3ee06ecf6aac": "Dışa aktarma başarısız oldu",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/docker.json
index 98557267..1fd2c22e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "Приховати публічні IPs",
"docker.privacy.mask-network-interfaces": "Приховати мережеві інтерфейси",
"docker.privacy.mask-external-urls": "Приховати зовнішні URL",
- "docker.actions.capture-diagnostics": "Діагностика захоплення сторінки"
+ "docker.actions.capture-diagnostics": "Діагностика захоплення сторінки",
+ "docker.folder.hide": "Приховати папку",
+ "docker.folder.restore": "Відновити папку",
+ "docker.folder.restore-hidden-branch": "Відновити приховану гілку",
+ "docker.folder.hidden-indicator": "Прихований",
+ "docker.folder.unnamed": "Папка",
+ "docker.folder.visibility-failed": "Не вдалося зберегти налаштування видимості папки.",
+ "docker.visibility.hidden-folders": "Приховані папки",
+ "docker.visibility.restore-all": "Відновити всі приховані папки",
+ "docker.visibility.hidden-title": "Папка прихована",
+ "docker.visibility.folder-fallback": "Папка",
+ "docker.visibility.hidden-message": "було приховано від перегляду Docker.",
+ "docker.visibility.undo": "Скасувати",
+ "docker.visibility.all-hidden-title": "Усі рядки папок приховано.",
+ "docker.visibility.all-hidden-help": "Розкрийте їх тимчасово або відновіть кожну приховану папку.",
+ "docker.visibility.show-hidden": "Показати приховані папки",
+ "docker.webui-profiles.open": "Відкрийте профіль WebUI",
+ "docker.webui-profiles.ready": "$1 ($2 з $3 готовий)",
+ "docker.webui-profiles.manage": "Керуйте профілями WebUI",
+ "docker.webui-profiles.none-title": "Немає готових WebUI",
+ "docker.webui-profiles.none-body": "Жоден із вибраних контейнерів не працює з доступним WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/editor.json
index 78a540db..1d5faa97 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "Скласти проект дорівнює",
"editor.rules.templates.compose-project-placeholder": "Назва проекту (приклад: ЗМІ)",
"editor.rules.templates.label-equals": "Мітка Docker дорівнює",
- "editor.rules.templates.label-equals-placeholder": "Мітка ключ=значення"
+ "editor.rules.templates.label-equals-placeholder": "Мітка ключ=значення",
+ "editor.webui-profiles.title": "Спеціальні профілі WebUI",
+ "editor.webui-profiles.help": "Створюйте багаторазові групи запуску з прямих учасників Docker цієї папки. Поточні адреси WebUI та запущений стан перевіряються лише під час відкриття профілю.",
+ "editor.webui-profiles.add": "Додати профіль",
+ "editor.webui-profiles.count": "$1 збережених профілів",
+ "editor.webui-profiles.empty": "Ще немає спеціальних профілів WebUI.",
+ "editor.webui-profiles.name": "Ім'я профілю",
+ "editor.webui-profiles.duplicate": "дублікат",
+ "editor.webui-profiles.move-up": "Рухатися вгору",
+ "editor.webui-profiles.move-down": "Рухатися вниз",
+ "editor.webui-profiles.delete": "Видалити",
+ "editor.webui-profiles.search": "Пошук учасників папки",
+ "editor.webui-profiles.select-webui": "Вибрати все за допомогою WebUI",
+ "editor.webui-profiles.clear": "Очистити вибір",
+ "editor.webui-profiles.webui-ready": "WebUI налаштовано",
+ "editor.webui-profiles.webui-runtime": "WebUI перевірено під час запуску",
+ "editor.webui-profiles.unavailable": "Недоступно в цій папці",
+ "editor.webui-profiles.no-members": "Немає відповідних учасників папки.",
+ "editor.webui-profiles.error-name": "Кожен профіль WebUI потребує імені.",
+ "editor.webui-profiles.error-duplicate": "Імена профілів WebUI мають бути унікальними в цій папці.",
+ "editor.webui-profiles.error-members": "Кожен профіль WebUI має вибрати принаймні одного члена папки.",
+ "editor.webui-profiles.limit": "Ця папка досягла обмеження безпеки профілю WebUI."
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/legacy-surface.json
index c718b1be..cde4dc22 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/uk/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "Жодних змін у програмі",
"legacy.surface.26f0360c9dddc4d1": "Попереднє налаштування видалено",
"legacy.surface.270bca2990500a60": "Виберіть місце розташування цієї папки в ієрархії.",
+ "legacy.surface.273c468809bda389": "Встановити кольори індикаторів і незалежний колір тексту статусу, який використовується рядками папок.",
"legacy.surface.273f838c039ee10f": "FolderView Plus виявив, що сторінка налаштувань стала порожньою до завершення ініціалізації.",
"legacy.surface.2753807153755a5e": "Об’єднати (додати + оновити)",
"legacy.surface.2847d4f584d08e0c": "Завантаження подання команд Docker...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "Початкова гілка ($1)",
"legacy.surface.32c919087b37ec2d": "Медіа стек",
"legacy.surface.334277e71d658735": "Після цілі",
+ "legacy.surface.33541731c2a77842": "Встановіть кольори індикаторів «Запущено», «Призупинено» та «Зупинено» незалежно від кольору тексту стану в режимах перегляду Docker, віртуальних машин і інформаційної панелі.",
"legacy.surface.3388c6d7238cdde6": "Назва налаштувань плагіна",
"legacy.surface.33891d013966c572": "Виберіть папку для масового призначення.",
"legacy.surface.33c5850b76f401e8": "Папка верхнього рівня",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "Виберіть зміну стану та учасників, якими вона керує.",
"legacy.surface.613aa3b591bacd9a": "Загальний обсяг пам’яті (диски з файловою підтримкою): $1",
"legacy.surface.61c56bacd53c2dfa": "Кольори шеврона:",
+ "legacy.surface.61ea108a3718792d": "Створюйте іменовані групи, які відкривають лише вибрані веб-інтерфейси контейнера Docker.",
"legacy.surface.61f565ba05bd647d": "Сюди буде автоматично додано відповідні назви контейнерів або віртуальних машин.",
"legacy.surface.61fcdf8c82cb687a": "Застосувати попередні завдання?",
"legacy.surface.6233afa7701a7fb8": "Включити показано",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "Ворушити",
"legacy.surface.7179ea0035fca783": "Очистити фільтри",
"legacy.surface.7188848298a0b500": "Сфера застосування: вибране",
+ "legacy.surface.71988c4d8e0803ba": "текст",
"legacy.surface.719e8555bf1abeba": "Скористайтеся кроком Шаблони, спочатку імпортуйте файли або створіть папки вручну.",
"legacy.surface.71b2e8d085cdca1e": "Готовий: всі перевірки пройшли.",
"legacy.surface.7205aff54d71cb8b": "Спершу скопіюйте налаштування папки з іншої папки Docker.",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "Попередній перегляд:",
"legacy.surface.75bf7d9afc988cb6": "Папки сортуються за назвою від А до Я. Вкладені папки все ще залишаються всередині батьківської гілки.",
"legacy.surface.75d599007fcf49af": "Всі членства",
- "legacy.surface.75d7a33335c4f033": "Встановіть кольори для початку, призупинення та зупинки для рядків папок.",
"legacy.surface.7611fe57a952da4b": "Рекомендовано. Автоматично блокує ризиковані зміни.",
"legacy.surface.764210a29b968296": "Категорія шаблону",
"legacy.surface.764b74dea7f342ff": "Контролює, скільки рівнів вкладених папок відображаються як фрагменти, коли дочірні папки відображаються у згорнутому попередньому перегляді.",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "Тіло таблиці VM",
"legacy.surface.8569ce7917f23eb6": "Видалити $1 вибраних правил?",
"legacy.surface.858e4ba7a29fd38b": "Пауза",
+ "legacy.surface.85b6c17862a72780": "Профілі WebUI",
"legacy.surface.85c83f4500bb9f2c": "Інформація про час виконання віртуальної машини",
"legacy.surface.85e2efe37bad1a44": "Немає придатних елементів для цієї дії.",
"legacy.surface.8600edb883562f2c": "Маскування IP-адрес локальної мережі",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "Проскануйте поточні налаштування папки Docker і віртуальної машини.",
"legacy.surface.916b423aa46d7493": "Виявлено $1 консультаційну(і) проблему(и) (глибина/порожня гілка), але немає жодних помилок посилання/помилки циклу для автоматичного відновлення.",
"legacy.surface.91f41a1797b442ab": "Оновлено $1 зі збереженого шаблону.",
- "legacy.surface.920b3e3ce5710d1d": "Установіть спеціальні кольори для піктограми стану цієї папки та тексту в режимах перегляду Docker, віртуальних машин і панелі інструментів.",
"legacy.surface.920e413c7d411b61": "Статус",
"legacy.surface.9213c7f4046c085d": "Попереджувальний поріг уваги",
"legacy.surface.921b15089caf4ff8": "3. Перегляньте порядок правил",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "Контролює, як незавершені оновлення впливають на стан цієї папки.",
"legacy.surface.926e4eab47bf42ea": "Збіги в реальному часі: введіть шаблон регулярного виразу.",
"legacy.surface.92c1cdfdf4cb9cf6": "Увімкнено",
+ "legacy.surface.92cff14b4b7992dd": "Виберіть прямі члени папки, відкриті кожним названим профілем.",
"legacy.surface.92ec9d61393d7064": "регулярний вираз $1",
"legacy.surface.93aa7d6a1b93167d": "Спеціальні піктограми не знайдено. Щоб почати, завантажте значок.",
"legacy.surface.93b1554a829a9433": "3 рівня",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "Незбережені зміни",
"legacy.surface.a71d508e50432f40": "Домашня лабораторія",
"legacy.surface.a7248eeb45eb1ce2": "нормальний",
- "legacy.surface.a759d24fa1d32402": "Відкрийте всі WebUI",
"legacy.surface.a75ee6a28c39f865": "Перетягуйте дочірні папки, щоб керувати порядком, показаним у попередньому перегляді та розгорнутих рядках.",
"legacy.surface.a76ac50faa110d7d": "Найстаріші створені папки спочатку",
"legacy.surface.a76e13b9839270eb": "Відновити",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "Сухий хід",
"legacy.surface.d640c7421da06661": "Резюме",
"legacy.surface.d66d27cf2aa666c8": "Вибір дисплея",
+ "legacy.surface.d693881b5ac21882": "--fvplus-текст-статусу папки",
"legacy.surface.d696a35bdd1883da": "Профіль",
"legacy.surface.d69b03bf0620f859": "Додайте наявні налаштування FolderView.",
"legacy.surface.d6a226a96bdc60fe": "Блокувати спливаючі вікна",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "Порівняйте",
"legacy.surface.e89b30aa1dc30a6a": "Ресурси",
"legacy.surface.e89c3109e1f14c41": "Цільова папка",
+ "legacy.surface.e8b4d99f2d672a8e": "Спеціальні профілі WebUI",
"legacy.surface.e8e470943fd0f0b6": "Порядок вкладених папок",
"legacy.surface.e948f182d485afee": "Розгорнути гілку",
"legacy.surface.e94d3ee06ecf6aac": "Помилка експорту",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/docker.json
index 525c758b..47962424 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "隐藏公共 IPs",
"docker.privacy.mask-network-interfaces": "隐藏网络接口",
"docker.privacy.mask-external-urls": "屏蔽外部网址",
- "docker.actions.capture-diagnostics": "捕获页面诊断"
+ "docker.actions.capture-diagnostics": "捕获页面诊断",
+ "docker.folder.hide": "隐藏文件夹",
+ "docker.folder.restore": "恢复文件夹",
+ "docker.folder.restore-hidden-branch": "恢复隐藏分支",
+ "docker.folder.hidden-indicator": "隐藏",
+ "docker.folder.unnamed": "文件夹",
+ "docker.folder.visibility-failed": "无法保存文件夹可见性首选项。",
+ "docker.visibility.hidden-folders": "隐藏文件夹",
+ "docker.visibility.restore-all": "恢复所有隐藏文件夹",
+ "docker.visibility.hidden-title": "文件夹隐藏",
+ "docker.visibility.folder-fallback": "文件夹",
+ "docker.visibility.hidden-message": "从 Docker 视图中隐藏了。",
+ "docker.visibility.undo": "撤消",
+ "docker.visibility.all-hidden-title": "所有文件夹行均被隐藏。",
+ "docker.visibility.all-hidden-help": "暂时显示它们或恢复每个隐藏文件夹。",
+ "docker.visibility.show-hidden": "显示隐藏文件夹",
+ "docker.webui-profiles.open": "打开WebUI配置文件",
+ "docker.webui-profiles.ready": "$1($3 的 $2 已准备好)",
+ "docker.webui-profiles.manage": "管理 WebUI 配置文件",
+ "docker.webui-profiles.none-title": "尚未准备好 WebUI",
+ "docker.webui-profiles.none-body": "所选容器均未使用可用的 WebUI 运行。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/editor.json
index 8a420648..09f9f8ab 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "撰写项目等于",
"editor.rules.templates.compose-project-placeholder": "项目名称(例如:媒体)",
"editor.rules.templates.label-equals": "Docker 标签等于",
- "editor.rules.templates.label-equals-placeholder": "标签键=值"
+ "editor.rules.templates.label-equals-placeholder": "标签键=值",
+ "editor.webui-profiles.title": "自定义 WebUI 配置文件",
+ "editor.webui-profiles.help": "从此文件夹的直接 Docker 成员创建可重用的启动组。仅当打开配置文件时才会检查当前 WebUI 地址和运行状态。",
+ "editor.webui-profiles.add": "添加个人资料",
+ "editor.webui-profiles.count": "$1 已保存的配置文件",
+ "editor.webui-profiles.empty": "尚无自定义 WebUI 配置文件。",
+ "editor.webui-profiles.name": "个人资料名称",
+ "editor.webui-profiles.duplicate": "重复",
+ "editor.webui-profiles.move-up": "向上移动",
+ "editor.webui-profiles.move-down": "下移",
+ "editor.webui-profiles.delete": "删除",
+ "editor.webui-profiles.search": "搜索文件夹成员",
+ "editor.webui-profiles.select-webui": "使用 WebUI 选择全部",
+ "editor.webui-profiles.clear": "清除选择",
+ "editor.webui-profiles.webui-ready": "WebUI已配置",
+ "editor.webui-profiles.webui-runtime": "WebUI 在启动时检查",
+ "editor.webui-profiles.unavailable": "此文件夹中不可用",
+ "editor.webui-profiles.no-members": "没有可用的匹配文件夹成员。",
+ "editor.webui-profiles.error-name": "每个 WebUI 配置文件都需要一个名称。",
+ "editor.webui-profiles.error-duplicate": "WebUI 配置文件名称在此文件夹中必须是唯一的。",
+ "editor.webui-profiles.error-members": "每个 WebUI 配置文件必须至少选择一个文件夹成员。",
+ "editor.webui-profiles.limit": "此文件夹已达到 WebUI 配置文件安全限制。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/legacy-surface.json
index d1439993..ccd912e1 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hans/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "没有应用程序更改",
"legacy.surface.26f0360c9dddc4d1": "预设已删除",
"legacy.surface.270bca2990500a60": "选择该文件夹在层次结构中的位置。",
+ "legacy.surface.273c468809bda389": "设置文件夹行使用的指示器颜色和独立状态文本颜色。",
"legacy.surface.273f838c039ee10f": "在初始化完成之前,FolderView Plus 检测到“设置”页面变为空白。",
"legacy.surface.2753807153755a5e": "合并(添加+更新)",
"legacy.surface.2847d4f584d08e0c": "正在加载 Docker 命令视图...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "开设分行 ($1)",
"legacy.surface.32c919087b37ec2d": "媒体堆栈",
"legacy.surface.334277e71d658735": "目标后",
+ "legacy.surface.33541731c2a77842": "在 Docker、VM 和仪表板视图中独立于状态文本颜色设置“已启动”、“已暂停”和“已停止”指示器颜色。",
"legacy.surface.3388c6d7238cdde6": "插件设置标题",
"legacy.surface.33891d013966c572": "选择用于批量分配的文件夹。",
"legacy.surface.33c5850b76f401e8": "顶级文件夹",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "选择状态更改及其控制的成员。",
"legacy.surface.613aa3b591bacd9a": "存储总量(文件备份磁盘):$1",
"legacy.surface.61c56bacd53c2dfa": "V 形颜色:",
+ "legacy.surface.61ea108a3718792d": "创建仅打开选定 Docker 容器 WebUI 的命名组。",
"legacy.surface.61f565ba05bd647d": "匹配的容器或虚拟机名称将自动包含在此处。",
"legacy.surface.61fcdf8c82cb687a": "应用预览的作业?",
"legacy.surface.6233afa7701a7fb8": "包括显示的",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "摆动",
"legacy.surface.7179ea0035fca783": "清除过滤器",
"legacy.surface.7188848298a0b500": "范围:收藏夹",
+ "legacy.surface.71988c4d8e0803ba": "文字",
"legacy.surface.719e8555bf1abeba": "首先使用模板步骤、导入文件或手动创建文件夹。",
"legacy.surface.71b2e8d085cdca1e": "准备就绪:所有检查均已通过。",
"legacy.surface.7205aff54d71cb8b": "首先从另一个 Docker 文件夹复制文件夹设置。",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "预览:",
"legacy.surface.75bf7d9afc988cb6": "文件夹按名称从 A 到 Z 排序。嵌套文件夹仍保留在其父分支内。",
"legacy.surface.75d599007fcf49af": "所有会员",
- "legacy.surface.75d7a33335c4f033": "设置文件夹行使用的开始、暂停和停止颜色。",
"legacy.surface.7611fe57a952da4b": "推荐。自动阻止有风险的更改。",
"legacy.surface.764210a29b968296": "模板类别",
"legacy.surface.764b74dea7f342ff": "控制当子文件夹显示在折叠预览中时,有多少嵌套文件夹级别显示为片。",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM桌体",
"legacy.surface.8569ce7917f23eb6": "删除 $1 个选定规则?",
"legacy.surface.858e4ba7a29fd38b": "暂停",
+ "legacy.surface.85b6c17862a72780": "WebUI 配置文件",
"legacy.surface.85c83f4500bb9f2c": "虚拟机运行时信息",
"legacy.surface.85e2efe37bad1a44": "没有符合此操作条件的项目。",
"legacy.surface.8600edb883562f2c": "掩码 LAN IP",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "扫描当前的 Docker 和 VM 文件夹设置。",
"legacy.surface.916b423aa46d7493": "检测到 $1 咨询问题(深度/空分支),但没有需要自动修复的孤立/循环链接错误。",
"legacy.surface.91f41a1797b442ab": "从已保存的模板更新了 $1。",
- "legacy.surface.920b3e3ce5710d1d": "跨 Docker、VM 和仪表板视图为此文件夹的状态图标和文本设置自定义颜色。",
"legacy.surface.920e413c7d411b61": "状态",
"legacy.surface.9213c7f4046c085d": "注意警告阈值",
"legacy.surface.921b15089caf4ff8": "3. 审核规则顺序",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "控制挂起的更新如何影响此文件夹的运行状况。",
"legacy.surface.926e4eab47bf42ea": "实时匹配:输入正则表达式模式。",
"legacy.surface.92c1cdfdf4cb9cf6": "启用",
+ "legacy.surface.92cff14b4b7992dd": "选择每个命名配置文件打开的直接文件夹成员。",
"legacy.surface.92ec9d61393d7064": "$1 正则表达式",
"legacy.surface.93aa7d6a1b93167d": "未找到自定义图标。上传图标即可开始。",
"legacy.surface.93b1554a829a9433": "3个级别",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "未保存的更改",
"legacy.surface.a71d508e50432f40": "家庭实验室",
"legacy.surface.a7248eeb45eb1ce2": "正常",
- "legacy.surface.a759d24fa1d32402": "打开所有WebUI",
"legacy.surface.a75ee6a28c39f865": "拖动子文件夹以控制预览和展开行中显示的顺序。",
"legacy.surface.a76ac50faa110d7d": "首先创建最早的文件夹",
"legacy.surface.a76e13b9839270eb": "恢复",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "试运行",
"legacy.surface.d640c7421da06661": "简历",
"legacy.surface.d66d27cf2aa666c8": "显示选项",
+ "legacy.surface.d693881b5ac21882": "--fvplus-文件夹状态文本",
"legacy.surface.d696a35bdd1883da": "公司简介",
"legacy.surface.d69b03bf0620f859": "引入现有的FolderView 设置。",
"legacy.surface.d6a226a96bdc60fe": "阻止弹出窗口",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "比较",
"legacy.surface.e89b30aa1dc30a6a": "资源",
"legacy.surface.e89c3109e1f14c41": "目标文件夹",
+ "legacy.surface.e8b4d99f2d672a8e": "自定义 WebUI 配置文件",
"legacy.surface.e8e470943fd0f0b6": "嵌套文件夹顺序",
"legacy.surface.e948f182d485afee": "拓展分支",
"legacy.surface.e94d3ee06ecf6aac": "导出失败",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/docker.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/docker.json
index 3181b846..85a6795b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/docker.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/docker.json
@@ -44,5 +44,25 @@
"docker.privacy.mask-public-ips": "遮蔽公共IP",
"docker.privacy.mask-network-interfaces": "遮蔽網路介面",
"docker.privacy.mask-external-urls": "封鎖外部 URL",
- "docker.actions.capture-diagnostics": "擷取頁面診斷"
+ "docker.actions.capture-diagnostics": "擷取頁面診斷",
+ "docker.folder.hide": "隱藏資料夾",
+ "docker.folder.restore": "恢復資料夾",
+ "docker.folder.restore-hidden-branch": "恢復隱藏分支",
+ "docker.folder.hidden-indicator": "隱藏",
+ "docker.folder.unnamed": "資料夾",
+ "docker.folder.visibility-failed": "無法儲存資料夾可見性首選項。",
+ "docker.visibility.hidden-folders": "隱藏資料夾",
+ "docker.visibility.restore-all": "恢復所有隱藏資料夾",
+ "docker.visibility.hidden-title": "資料夾隱藏",
+ "docker.visibility.folder-fallback": "資料夾",
+ "docker.visibility.hidden-message": "從 Docker 檢視中隱藏了。",
+ "docker.visibility.undo": "撤銷",
+ "docker.visibility.all-hidden-title": "所有資料夾行均被隱藏。",
+ "docker.visibility.all-hidden-help": "暫時顯示它們或恢復每個隱藏資料夾。",
+ "docker.visibility.show-hidden": "顯示隱藏資料夾",
+ "docker.webui-profiles.open": "開啟WebUI設定檔",
+ "docker.webui-profiles.ready": "$1($3 的 $2 已準備好)",
+ "docker.webui-profiles.manage": "管理 WebUI 設定檔案",
+ "docker.webui-profiles.none-title": "尚未準備好 WebUI",
+ "docker.webui-profiles.none-body": "所選容器均未使用可用的 WebUI 運作。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/editor.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/editor.json
index b37aa9cc..9c9d8b57 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/editor.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/editor.json
@@ -27,5 +27,26 @@
"editor.rules.templates.compose-project": "撰寫專案等於",
"editor.rules.templates.compose-project-placeholder": "專案名稱(例如:媒體)",
"editor.rules.templates.label-equals": "Docker 標籤等於",
- "editor.rules.templates.label-equals-placeholder": "標籤鍵=值"
+ "editor.rules.templates.label-equals-placeholder": "標籤鍵=值",
+ "editor.webui-profiles.title": "自訂 WebUI 設定檔案",
+ "editor.webui-profiles.help": "從此資料夾的直接 Docker 成員建立可重複使用的啟動群組。僅當開啟設定檔時才會檢查目前 WebUI 位址和運作狀態。",
+ "editor.webui-profiles.add": "新增個人資料",
+ "editor.webui-profiles.count": "$1 已儲存的設定檔",
+ "editor.webui-profiles.empty": "尚無自訂 WebUI 設定檔。",
+ "editor.webui-profiles.name": "個人資料名稱",
+ "editor.webui-profiles.duplicate": "重複",
+ "editor.webui-profiles.move-up": "向上移動",
+ "editor.webui-profiles.move-down": "下移",
+ "editor.webui-profiles.delete": "刪除",
+ "editor.webui-profiles.search": "搜尋資料夾成員",
+ "editor.webui-profiles.select-webui": "使用 WebUI 選擇全部",
+ "editor.webui-profiles.clear": "清除選擇",
+ "editor.webui-profiles.webui-ready": "WebUI已配置",
+ "editor.webui-profiles.webui-runtime": "WebUI 在啟動時檢查",
+ "editor.webui-profiles.unavailable": "此資料夾中不可用",
+ "editor.webui-profiles.no-members": "沒有可用的匹配資料夾成員。",
+ "editor.webui-profiles.error-name": "每個 WebUI 設定檔都需要一個名稱。",
+ "editor.webui-profiles.error-duplicate": "WebUI 設定檔名稱在此資料夾中必須是唯一的。",
+ "editor.webui-profiles.error-members": "每個 WebUI 設定檔必須至少選擇一個資料夾成員。",
+ "editor.webui-profiles.limit": "此資料夾已達到 WebUI 設定檔安全限制。"
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/legacy-surface.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/legacy-surface.json
index ad907ba6..09c65349 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/legacy-surface.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/namespaces/zh-Hant/legacy-surface.json
@@ -263,6 +263,7 @@
"legacy.surface.26d5ea59655eced7": "沒有應用程式更改",
"legacy.surface.26f0360c9dddc4d1": "預設已刪除",
"legacy.surface.270bca2990500a60": "選擇該資料夾在層次結構中的位置。",
+ "legacy.surface.273c468809bda389": "設定資料夾行使用的指示器顏色和獨立狀態文本顏色。",
"legacy.surface.273f838c039ee10f": "在初始化完成之前,FolderView Plus 偵測到「設定」頁面變為空白。",
"legacy.surface.2753807153755a5e": "合併(新增+更新)",
"legacy.surface.2847d4f584d08e0c": "正在載入 Docker 命令檢視...",
@@ -330,6 +331,7 @@
"legacy.surface.32c1b75695ad6dc6": "啟動分支 ($1)",
"legacy.surface.32c919087b37ec2d": "媒體堆疊",
"legacy.surface.334277e71d658735": "目標後",
+ "legacy.surface.33541731c2a77842": "在 Docker、VM 和儀表板檢視中獨立於狀態文本顏色設定“已啟動”、“已暫停”和“已停止”指示器顏色。",
"legacy.surface.3388c6d7238cdde6": "外掛設定標題",
"legacy.surface.33891d013966c572": "選擇用於批次分配的資料夾。",
"legacy.surface.33c5850b76f401e8": "頂級資料夾",
@@ -596,6 +598,7 @@
"legacy.surface.61376189cb6f261d": "選擇狀態變更及其控制的成員。",
"legacy.surface.613aa3b591bacd9a": "儲存總量(檔案備份磁碟):$1",
"legacy.surface.61c56bacd53c2dfa": "V 型顏色:",
+ "legacy.surface.61ea108a3718792d": "建立僅開啟選定 Docker 容器 WebUI 的命名組。",
"legacy.surface.61f565ba05bd647d": "相符的容器或虛擬機器名稱將自動包含在此。",
"legacy.surface.61fcdf8c82cb687a": "應用預覽的作業?",
"legacy.surface.6233afa7701a7fb8": "包括顯示的",
@@ -696,6 +699,7 @@
"legacy.surface.715c14763cf732e6": "擺動",
"legacy.surface.7179ea0035fca783": "清除過濾器",
"legacy.surface.7188848298a0b500": "範圍:收藏夾",
+ "legacy.surface.71988c4d8e0803ba": "文字",
"legacy.surface.719e8555bf1abeba": "首先使用範本步驟、匯入檔案或手動建立資料夾。",
"legacy.surface.71b2e8d085cdca1e": "準備就緒:所有檢查均已通過。",
"legacy.surface.7205aff54d71cb8b": "首先從另一個 Docker 資料夾複製資料夾設定。",
@@ -725,7 +729,6 @@
"legacy.surface.75954c5f4d87da20": "預覽:",
"legacy.surface.75bf7d9afc988cb6": "資料夾按名稱從 A 到 Z 排序。巢狀資料夾仍保留在其父分支內。",
"legacy.surface.75d599007fcf49af": "所有會員",
- "legacy.surface.75d7a33335c4f033": "設定資料夾行使用的開始、暫停和停止顏色。",
"legacy.surface.7611fe57a952da4b": "推薦。自動阻止有風險的變更。",
"legacy.surface.764210a29b968296": "模板類別",
"legacy.surface.764b74dea7f342ff": "控制當子資料夾顯示在折疊預覽中時,有多少巢狀資料夾層級顯示為片。",
@@ -811,6 +814,7 @@
"legacy.surface.8567ea7fe71960df": "VM桌體",
"legacy.surface.8569ce7917f23eb6": "刪除 $1 選取的規則嗎?",
"legacy.surface.858e4ba7a29fd38b": "暫停",
+ "legacy.surface.85b6c17862a72780": "WebUI 設定檔",
"legacy.surface.85c83f4500bb9f2c": "虛擬機器執行時資訊",
"legacy.surface.85e2efe37bad1a44": "沒有符合此操作條件的專案。",
"legacy.surface.8600edb883562f2c": "遮罩 LAN IP",
@@ -884,7 +888,6 @@
"legacy.surface.916396e7343d31fd": "掃描目前的 Docker 和 VM 資料夾設定。",
"legacy.surface.916b423aa46d7493": "偵測到 $1 諮詢問題(深度/空分支),但沒有需要自動修復的孤立/迴圈連結錯誤。",
"legacy.surface.91f41a1797b442ab": "從已儲存的模板更新了 $1。",
- "legacy.surface.920b3e3ce5710d1d": "跨 Docker、VM 和儀錶板檢視為此資料夾的狀態圖示和文字設定自訂顏色。",
"legacy.surface.920e413c7d411b61": "狀態",
"legacy.surface.9213c7f4046c085d": "注意警告閾值",
"legacy.surface.921b15089caf4ff8": "3. 審核規則順序",
@@ -892,6 +895,7 @@
"legacy.surface.924a59a8bcb1c176": "控制掛起的更新如何影響此資料夾的運作狀況。",
"legacy.surface.926e4eab47bf42ea": "即時比對:輸入正規表示式模式。",
"legacy.surface.92c1cdfdf4cb9cf6": "啟用",
+ "legacy.surface.92cff14b4b7992dd": "選擇每個命名配置檔案開啟的直接資料夾成員。",
"legacy.surface.92ec9d61393d7064": "$1 正規表示式",
"legacy.surface.93aa7d6a1b93167d": "未找到自訂圖示。上傳圖示即可開始。",
"legacy.surface.93b1554a829a9433": "3個級別",
@@ -1028,7 +1032,6 @@
"legacy.surface.a710c2b90913b537": "未儲存的更改",
"legacy.surface.a71d508e50432f40": "家庭實驗室",
"legacy.surface.a7248eeb45eb1ce2": "正常",
- "legacy.surface.a759d24fa1d32402": "開啟所有WebUI",
"legacy.surface.a75ee6a28c39f865": "拖曳子資料夾以控制預覽和展開行中顯示的順序。",
"legacy.surface.a76ac50faa110d7d": "首先建立最早的資料夾",
"legacy.surface.a76e13b9839270eb": "恢復",
@@ -1294,6 +1297,7 @@
"legacy.surface.d5da154d9f00ca14": "試執行",
"legacy.surface.d640c7421da06661": "履歷",
"legacy.surface.d66d27cf2aa666c8": "顯示選項",
+ "legacy.surface.d693881b5ac21882": "--fvplus-資料夾狀態文本",
"legacy.surface.d696a35bdd1883da": "公司簡介",
"legacy.surface.d69b03bf0620f859": "引入現有的FolderView 設定。",
"legacy.surface.d6a226a96bdc60fe": "阻止彈出視窗",
@@ -1413,6 +1417,7 @@
"legacy.surface.e888155d8c7d2e3c": "比較",
"legacy.surface.e89b30aa1dc30a6a": "資源",
"legacy.surface.e89c3109e1f14c41": "目標資料夾",
+ "legacy.surface.e8b4d99f2d672a8e": "自訂 WebUI 設定檔案",
"legacy.surface.e8e470943fd0f0b6": "巢狀資料夾順序",
"legacy.surface.e948f182d485afee": "拓展分支",
"legacy.surface.e94d3ee06ecf6aac": "匯出失敗",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nb.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nb.json
index bed6efa9..aabbe43e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nb.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nb.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "nb",
"native-name": "Norsk bokmål",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Legg til mappe",
"up-to-date": "oppdatert",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nl.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nl.json
index cf503b70..5c4ed5d9 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nl.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/nl.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "nl",
"native-name": "Nederlands",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Map Toevoegen",
"up-to-date": "bijgewerkt",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pl.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pl.json
index dec86687..9b308d32 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pl.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pl.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "pl",
"native-name": "Polski",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Dodaj folder",
"up-to-date": "aktualny",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-BR.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-BR.json
index 05035bb2..60974d4f 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-BR.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-BR.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "pt-BR",
"native-name": "Português (Brasil)",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Adicionar pasta",
"up-to-date": "atualizado",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-PT.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-PT.json
index 86520298..05c2627c 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-PT.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/pt-PT.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "pt-PT",
"native-name": "Português (Portugal)",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Adicionar Pasta",
"up-to-date": "atualizado",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ro.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ro.json
index e834a466..e086bdec 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ro.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ro.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ro",
"native-name": "Română",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Adaugă folder",
"up-to-date": "actualizat",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ru.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ru.json
index 8945f3b9..412434fb 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ru.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/ru.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "ru",
"native-name": "Русский",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Добавить папку",
"up-to-date": "актуально",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/sv.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/sv.json
index 3f463c7b..dea243c9 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/sv.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/sv.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "sv",
"native-name": "Svenska",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Lägg till mapp",
"up-to-date": "uppdaterad",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/tr.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/tr.json
index 3c58dc12..fa19f4bc 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/tr.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/tr.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "tr",
"native-name": "Türkçe",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Klasör Ekle",
"up-to-date": "güncel",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/uk.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/uk.json
index 47a24995..ba07d33e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/uk.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/uk.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "uk",
"native-name": "Українська",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "Додати папку",
"up-to-date": "актуально",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hans.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hans.json
index 5b2df1e1..2a0f7432 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hans.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hans.json
@@ -5,15 +5,15 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "zh-Hans",
"native-name": "简体中文",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "添加文件夹",
"up-to-date": "最新",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hant.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hant.json
index f1f2ce50..c3fd6cfe 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hant.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/langs/zh-Hant.json
@@ -5,16 +5,16 @@
],
"catalog-version": "2026.08.10.1",
"direction": "ltr",
- "last-reviewed": "2026-08-18",
- "last-updated": "2026-08-18",
+ "last-reviewed": "2026-08-28",
+ "last-updated": "2026-08-28",
"locale": "zh-Hant",
"native-name": "繁體中文",
"normalization-profile": "opencc-s2twp-1.4.1",
"reviewed": true,
"source-revision": "2026.08.10.1",
"status": "complete",
- "translated-messages": 2122,
- "total-source-messages": 2122
+ "translated-messages": 2168,
+ "total-source-messages": 2168
},
"add-folder": "新增資料夾",
"up-to-date": "最新的",
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/schemas/architecture-contracts.schema.json b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/schemas/architecture-contracts.schema.json
index 617fef89..c86bc1a0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/schemas/architecture-contracts.schema.json
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/schemas/architecture-contracts.schema.json
@@ -19,6 +19,7 @@
"scripts/folderviewplus.utils.js": { "limit": 88, "target": 88, "history": [2346, 2054, 88] },
"scripts/folderviewplus.utils-normalization.js": { "limit": 443, "target": 400, "history": [452, 443] },
"scripts/folderviewplus.utils-prefs.js": { "limit": 383, "target": 380, "history": [448, 383] },
+ "scripts/folder.webui-profiles.js": { "limit": 447, "target": 400, "history": [447] },
"scripts/folderviewplus.utils-ordering.js": { "limit": 185, "target": 180, "history": [270, 185] },
"scripts/folderviewplus.utils-transfer.js": { "limit": 645, "target": 620, "history": [717, 645] },
"scripts/folderviewplus.utils-rules.js": { "limit": 533, "target": 500, "history": [598, 533] },
@@ -47,6 +48,7 @@
"server/lib.diagnostics-integrity.php": { "limit": 741, "target": 400, "history": [741] },
"server/lib.diagnostics-redaction.php": { "limit": 495, "target": 400, "history": [495] },
"server/lib.diagnostics-summary.php": { "limit": 785, "target": 400, "history": [785] },
+ "server/lib.webui-profiles.php": { "limit": 128, "target": 120, "history": [128] },
"server/lib.diagnostics-support-bundle.php": { "limit": 657, "target": 400, "history": [657] },
"server/lib.docker-order.php": { "limit": 437, "target": 380, "history": [437] },
"server/lib.docker-start-order-sequence.php": { "limit": 56, "target": 56, "history": [56] },
@@ -146,7 +148,7 @@
"kind": "browser-entrypoint",
"stateModel": "entrypoint-owned",
"consumers": ["docker"],
- "dependsOn": ["scripts/folderviewplus.utils.js", "scripts/folderviewplus.request.js", "scripts/runtime.host-adapter.js", "scripts/runtime.folder-ordering.js", "scripts/runtime.live-refresh.js", "scripts/runtime.page-diagnostics.js", "scripts/docker.runtime.shared.js", "scripts/docker.runtime.container-model.js", "scripts/docker.runtime.folder-grouping.js", "scripts/docker.runtime.capabilities.js", "scripts/docker.runtime.providers.js", "scripts/docker.runtime.api-coordinator.js", "scripts/docker.runtime.refresh-diagnostics.js", "scripts/docker.runtime.column-controller.js", "scripts/docker.runtime.provider-health.js"],
+ "dependsOn": ["scripts/folderviewplus.utils.js", "scripts/folderviewplus.request.js", "scripts/runtime.host-adapter.js", "scripts/runtime.folder-ordering.js", "scripts/runtime.live-refresh.js", "scripts/runtime.page-diagnostics.js", "scripts/docker.runtime.shared.js", "scripts/docker.runtime.container-model.js", "scripts/docker.runtime.folder-grouping.js", "scripts/docker.runtime.capabilities.js", "scripts/docker.runtime.providers.js", "scripts/docker.runtime.api-coordinator.js", "scripts/docker.runtime.refresh-diagnostics.js", "scripts/docker.runtime.column-controller.js", "scripts/docker.runtime.provider-health.js", "scripts/docker.runtime.child-folder-preview-menu.js", "scripts/docker.runtime.hidden-folders.js", "scripts/folder.webui-profiles.js"],
"lifecycle": { "startup": "host-bootstrap", "teardown": "pagehide-owned" },
"compatibilityGlobals": ["addDockerFolderContext", "dropDownButton", "editFolder", "createFolderBtn"]
},
@@ -176,7 +178,7 @@
"kind": "browser-entrypoint",
"stateModel": "entrypoint-owned",
"consumers": ["folder-editor"],
- "dependsOn": ["scripts/folderviewplus.utils.js", "scripts/folderviewplus.request.js", "scripts/folder.editor.shared.js", "scripts/folder.editor.schema.js", "scripts/folder.editor.state.js", "scripts/folder.editor.members.js", "scripts/folder.editor.hierarchy.js", "scripts/folder.editor.regex-selection.js", "scripts/folder.editor.member-list.js", "scripts/folder.editor.legacy-rule-model.js", "scripts/folderviewplus.rule-templates.js", "scripts/folder.editor.rule-template-workspace.js", "scripts/folder.editor.type-docker.js", "scripts/folder.editor.type-vm.js"],
+ "dependsOn": ["scripts/folderviewplus.utils.js", "scripts/folderviewplus.request.js", "scripts/folder.editor.shared.js", "scripts/folder.editor.schema.js", "scripts/folder.editor.state.js", "scripts/folder.editor.members.js", "scripts/folder.editor.hierarchy.js", "scripts/folder.editor.regex-selection.js", "scripts/folder.editor.member-list.js", "scripts/folder.webui-profiles.js", "scripts/folder.editor.legacy-rule-model.js", "scripts/folderviewplus.rule-templates.js", "scripts/folder.editor.rule-template-workspace.js", "scripts/folder.editor.type-docker.js", "scripts/folder.editor.type-vm.js"],
"lifecycle": { "startup": "loader-managed-bootstrap", "teardown": "pagehide-owned" },
"compatibilityGlobals": ["submitForm", "updateForm", "updateRegex", "customAction"]
},
@@ -196,7 +198,7 @@
"kind": "server-facade",
"stateModel": "request-scoped",
"consumers": ["plugin-pages", "server-endpoints"],
- "dependsOn": ["server/lib.remote.php", "server/lib.process.php", "server/lib.filesystem-security.php", "server/lib.security.php", "server/lib.request-authority.php", "server/lib.api-contract.php", "server/lib.preflight.php", "server/lib.prefs.php", "server/lib.diagnostics.php", "server/lib.docker-runtime.php", "server/lib.input-security.php", "server/lib.release-notes.php", "server/lib.update-channel.php", "server/lib.storage.php", "server/lib.theme-profiles.php", "server/lib.theme-update-plan.php", "server/lib.theme-workspace.php", "server/lib.environment-snapshot.php", "server/lib.environment-transaction.php", "server/lib.folderview3-migration.php", "server/lib.folderview3-apply.php", "server/lib.theme-github.php", "server/lib.theme-profile-actions.php", "server/lib.theme-bulk-update.php", "server/lib.backup-schedule.php", "server/lib.backup-snapshots.php", "server/lib.folder-rules.php", "server/lib.docker-start-order-sequence.php", "server/lib.docker-order.php", "server/lib.folder-mutations.php", "server/lib.custom-icon-storage.php", "server/lib.runtime-info.php"],
+ "dependsOn": ["server/lib.remote.php", "server/lib.process.php", "server/lib.filesystem-security.php", "server/lib.security.php", "server/lib.request-authority.php", "server/lib.api-contract.php", "server/lib.preflight.php", "server/lib.webui-profiles.php", "server/lib.prefs.php", "server/lib.diagnostics.php", "server/lib.docker-runtime.php", "server/lib.input-security.php", "server/lib.release-notes.php", "server/lib.update-channel.php", "server/lib.storage.php", "server/lib.theme-profiles.php", "server/lib.theme-update-plan.php", "server/lib.theme-workspace.php", "server/lib.environment-snapshot.php", "server/lib.environment-transaction.php", "server/lib.folderview3-migration.php", "server/lib.folderview3-apply.php", "server/lib.theme-github.php", "server/lib.theme-profile-actions.php", "server/lib.theme-bulk-update.php", "server/lib.backup-schedule.php", "server/lib.backup-snapshots.php", "server/lib.folder-rules.php", "server/lib.docker-start-order-sequence.php", "server/lib.docker-order.php", "server/lib.folder-mutations.php", "server/lib.custom-icon-storage.php", "server/lib.runtime-info.php"],
"lifecycle": { "startup": "require-once", "teardown": "request-end" },
"compatibilityGlobals": []
},
@@ -232,6 +234,16 @@
"loadedBy": "server/lib.php",
"functionInventory": { "count": 11, "sha256": "4b55393420a54ede2692f9513c50d19aa31a49e4950bcbd155c3de720294b254" }
},
+ {
+ "file": "server/lib.webui-profiles.php",
+ "owner": "server-folder-webui-profiles",
+ "consumers": ["plugin-pages", "server-endpoints"],
+ "stateModel": "request-scoped",
+ "dependsOn": [],
+ "compatibilityGlobals": [],
+ "loadedBy": "server/lib.php",
+ "functionInventory": { "count": 5, "sha256": "4f00673340cef720edd4334bc1c56fffa0704ae017057c204fce07d0fe61afe8" }
+ },
{
"file": "server/lib.release-notes.php",
"owner": "server-release-metadata",
@@ -417,7 +429,7 @@
"owner": "server-folder-mutations",
"consumers": ["server-endpoints"],
"stateModel": "request-scoped",
- "dependsOn": ["server/lib.storage.php", "server/lib.backup-snapshots.php", "server/lib.docker-order.php"],
+ "dependsOn": ["server/lib.storage.php", "server/lib.backup-snapshots.php", "server/lib.docker-order.php", "server/lib.webui-profiles.php"],
"compatibilityGlobals": [],
"loadedBy": "server/lib.php",
"functionInventory": { "count": 7, "sha256": "ed85e1dd77855f1a445e2029e0c581c3fbf55df7477571e4538614bcac35824f" }
@@ -467,7 +479,7 @@
"owner": "server-diagnostics-summary",
"consumers": ["server-endpoints"],
"stateModel": "request-scoped",
- "dependsOn": ["server/lib.diagnostics-integrity.php"],
+ "dependsOn": ["server/lib.diagnostics-integrity.php", "server/lib.webui-profiles.php"],
"compatibilityGlobals": [],
"loadedBy": "server/lib.diagnostics.php",
"functionInventory": { "count": 12, "sha256": "a58ee2ba2b616c31d868a9736a527f2647589af5bee06c91bcd231e6010fe9d7" }
@@ -870,6 +882,22 @@
{ "name": "createApi", "exports": ["setChildFolderOrder", "getChildFolderOrderIds", "updateList", "dispose"] }
]
},
+ {
+ "file": "scripts/folder.webui-profiles.js",
+ "owner": "shared-folder-webui-profiles",
+ "consumers": ["docker", "vm", "dashboard", "folder-editor"],
+ "stateModel": "factory-owned",
+ "dependsOn": [],
+ "compatibilityGlobals": ["FolderViewPlusFoundationModules"],
+ "global": "FolderViewPlusFoundationModules",
+ "globalMember": "folderWebuiProfiles",
+ "loader": "commonjs",
+ "typeChecked": true,
+ "exports": ["MAX_PROFILES", "MAX_PROFILE_MEMBERS", "MAX_PROFILE_NAME_LENGTH", "normalizeProfiles", "stripProfilesFromSettings", "renameProfileMember", "collectProfileTargets", "summarizeProfiles", "resolveProfileLaunch", "appendRuntimeMenuItems", "createEditorApi"],
+ "lifecycleFactories": [
+ { "name": "createEditorApi", "exports": ["hydrate", "render", "refreshMembers", "serialize", "validate", "snapshot", "dispose"] }
+ ]
+ },
{
"file": "scripts/docker.runtime.column-controller.js",
"owner": "docker-runtime-layout",
@@ -944,6 +972,22 @@
{ "name": "createIntegration", "exports": ["prepare", "refresh", "tick", "status", "dispose"] }
]
},
+ {
+ "file": "scripts/docker.runtime.child-folder-preview-menu.js",
+ "owner": "docker-child-folder-preview-menu",
+ "consumers": ["docker"],
+ "stateModel": "factory-owned",
+ "dependsOn": [],
+ "compatibilityGlobals": ["FolderViewPlusFoundationModules"],
+ "global": "FolderViewPlusFoundationModules",
+ "globalMember": "dockerChildFolderPreviewMenu",
+ "loader": "commonjs",
+ "typeChecked": true,
+ "exports": ["createApi"],
+ "lifecycleFactories": [
+ { "name": "createApi", "exports": ["close", "show"] }
+ ]
+ },
{
"file": "scripts/docker.runtime.layout-geometry.js",
"owner": "docker-runtime-layout",
@@ -1211,6 +1255,20 @@
"lifecycleFactories": [
{ "name": "createDeferredPreviewController", "exports": ["start", "refresh", "destroy", "snapshot"] }
]
+ },
+ {
+ "file": "scripts/docker.runtime.hidden-folders.js",
+ "owner": "docker-hidden-folder-visibility",
+ "consumers": ["docker"],
+ "stateModel": "factory-owned",
+ "dependsOn": [],
+ "compatibilityGlobals": ["FolderViewPlusFoundationModules"],
+ "global": "FolderViewPlusFoundationModules",
+ "globalMember": "dockerHiddenFolders",
+ "loader": "commonjs",
+ "exports": ["createApi"],
+ "apiFactory": "createApi",
+ "apiExports": ["normalizeIds", "reconcilePrefs", "getOwnerId", "getSummary", "applyVisibility", "hideFolder", "restoreFolder", "restoreAll", "setReveal", "buildQuickAction", "decorateQuickIcon", "svgIcon"]
}
],
"browserGlobals": {
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js
index b68b1afc..e8a1aa13 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/dashboard.js
@@ -35,9 +35,10 @@ const dashboardT = (key, fallback = '', ...params) => (
window.FolderViewPlusI18n?.t(key, fallback, ...params) || fallback || key
);
const localDefaultFolderStatusColors = {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d',
+ text: '#ffffff'
};
const localResolvedFolderStatusColors = {
started: 'var(--fvplus-folder-status-started, var(--fvplus-status-started, var(--fvplus-theme-foreground, currentColor)))',
@@ -82,15 +83,14 @@ const resolveDashboardFolderStatusColors = (settings) => {
return colors;
}
return {
- started: colors.started === localDefaultFolderStatusColors.started
- ? localResolvedFolderStatusColors.started
- : colors.started,
+ started: colors.started,
paused: colors.paused === localDefaultFolderStatusColors.paused
? localResolvedFolderStatusColors.paused
: colors.paused,
stopped: colors.stopped === localDefaultFolderStatusColors.stopped
? localResolvedFolderStatusColors.stopped
- : colors.stopped
+ : colors.stopped,
+ text: colors.text || localDefaultFolderStatusColors.text
};
};
const isFolderAccentEnabled = typeof folderContract?.isFolderAccentEnabled === 'function'
@@ -158,7 +158,8 @@ const utils = window.FolderViewPlusUtils || {
return {
started: normalizeStatusHexColor(incoming.status_color_started, localDefaultFolderStatusColors.started),
paused: normalizeStatusHexColor(incoming.status_color_paused, localDefaultFolderStatusColors.paused),
- stopped: normalizeStatusHexColor(incoming.status_color_stopped, localDefaultFolderStatusColors.stopped)
+ stopped: normalizeStatusHexColor(incoming.status_color_stopped, localDefaultFolderStatusColors.stopped),
+ text: normalizeStatusHexColor(incoming.status_color_text, localDefaultFolderStatusColors.text)
};
},
escapeHtml: (value) => String(value ?? '')
@@ -1956,7 +1957,7 @@ const createFolderDocker = (folder, id, position, order, containersInfo, folders
const $statusIcon = sel.next('span.inner').children('i');
const $statusText = sel.next('span.inner').children('span.state');
$statusIcon.css('color', statusColors.stopped);
- $statusText.css('color', statusColors.stopped);
+ $statusText.css('color', statusColors.text);
//set the status of a folder
@@ -1974,7 +1975,7 @@ const createFolderDocker = (folder, id, position, order, containersInfo, folders
const $nextStatusIcon = $(``);
$nextStatusIcon.css('color', statusColor);
$statusIcon.replaceWith($nextStatusIcon);
- $statusText.text(`${started}/${Object.entries(folder.containers).length} ${statusLabel}`).css('color', statusColor);
+ $statusText.text(`${started}/${Object.entries(folder.containers).length} ${statusLabel}`).css('color', statusColors.text);
}
if(autostart === 0) {
@@ -2220,7 +2221,7 @@ const createFolderVM = (folder, id, position, order, vmInfo, foldersDone, matchC
const $statusIcon = sel.next('span.inner').children('i');
const $statusText = sel.next('span.inner').children('span.state');
$statusIcon.css('color', statusColors.stopped);
- $statusText.css('color', statusColors.stopped);
+ $statusText.css('color', statusColors.text);
if (started) {
const allStartedArePaused = paused > 0 && paused === started;
const statusClass = allStartedArePaused ? 'paused' : 'started';
@@ -2231,7 +2232,7 @@ const createFolderVM = (folder, id, position, order, vmInfo, foldersDone, matchC
const $nextStatusIcon = $(``);
$nextStatusIcon.css('color', statusColor);
$statusIcon.replaceWith($nextStatusIcon);
- $statusText.text(`${started}/${Object.entries(folder.containers).length} ${statusLabel}`).css('color', statusColor);
+ $statusText.text(`${started}/${Object.entries(folder.containers).length} ${statusLabel}`).css('color', statusColors.text);
}
if(autostart === 0) {
@@ -2689,7 +2690,7 @@ const updateDashboardFolderRuntimeSummary = (type, id, folder) => {
.addClass(`fa ${aggregate.icon} ${aggregate.className}`)
.removeAttr('aria-busy')
.css('color', statusColor);
- $statusText.text(`${aggregate.count}/${total} ${$.i18n(aggregate.key)}`).css('color', statusColor);
+ $statusText.text(`${aggregate.count}/${total} ${$.i18n(aggregate.key)}`).css('color', statusColors.text);
$outer.add($folderSurface).removeClass('no-autostart autostart-off autostart-partial autostart-full no-managed managed-partial managed-full');
if (autostart === 0) $outer.add($folderSurface).addClass('no-autostart');
else if (autostartStarted === 0) $outer.add($folderSurface).addClass('autostart-off');
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js
index 619b5ba7..3d35fe5e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js
@@ -63,9 +63,11 @@ const dockerFolderGroupingModule = window.FolderViewPlusFoundationModules?.docke
const dockerApiCoordinatorModule = window.FolderViewPlusFoundationModules?.dockerApiCoordinator || null;
let dockerApiIntegration = null;
const dockerPreviewActionsModule = window.FolderViewPlusDockerPreviewActions || null;
+const dockerChildFolderPreviewMenuModule = window.FolderViewPlusFoundationModules?.dockerChildFolderPreviewMenu || null;
const dockerRuntimeHierarchyModule = window.FolderViewPlusDockerRuntimeHierarchy || null;
const folderPreviewModelModule = window.FolderViewPlusFolderPreviewModel || null;
const memberIdentityModule = window.FolderViewPlusMemberIdentity || null;
+const folderWebuiProfilesModule = window.FolderViewPlusFoundationModules?.folderWebuiProfiles || null;
const dockerRuntimeActionsModule = window.FolderViewPlusDockerRuntimeActions || null;
const dockerHostGuardsModule = window.FolderViewPlusDockerHostGuards || null;
const dockerRuntimeDiagnosticsModule = window.FolderViewPlusDockerRuntimeDiagnostics || null;
@@ -81,9 +83,9 @@ const applyDockerThemeResolverTokens = (reason = 'docker-runtime:initial', optio
: null
);
const localDefaultFolderStatusColors = dockerRuntimeShared.DEFAULT_FOLDER_STATUS_COLORS || {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d', text: '#ffffff'
};
const applyFolderStatusColorOverrides = typeof dockerRuntimeShared.applyFolderStatusColorOverrides === 'function'
? dockerRuntimeShared.applyFolderStatusColorOverrides
@@ -205,6 +207,7 @@ const utils = window.FolderViewPlusUtils || {
normalizePrefs: () => ({
sortMode: 'created',
manualOrder: [],
+ hiddenFolderIds: [],
hideEmptyFolders: false,
appColumnWidth: 'standard',
autoRules: [],
@@ -444,6 +447,12 @@ if (
} else {
setDockerFatalBannerModuleStatus('docker.runtime.preview-actions.js', 'ok', 'Docker preview action helpers ready');
}
+if (!dockerChildFolderPreviewMenuModule || typeof dockerChildFolderPreviewMenuModule.createApi !== 'function') {
+ dockerBootstrapMissingModules.push('docker.runtime.child-folder-preview-menu.js');
+ setDockerFatalBannerModuleStatus('docker.runtime.child-folder-preview-menu.js', 'missing', 'child-folder preview menu unavailable');
+} else {
+ setDockerFatalBannerModuleStatus('docker.runtime.child-folder-preview-menu.js', 'ok', 'child-folder preview menu ready');
+}
if (
window.FolderViewPlusFolderPreviewModelModuleLoaded !== true
|| !folderPreviewModelModule
@@ -693,6 +702,7 @@ let dockerRuntimeInfoByName = {};
let dockerRuntimeInfoApi = null;
let dockerPreviewActionsApi = null;
let dockerRuntimeHierarchyApi = null;
+const dockerAdvancedPreviewContextBindersByName = new Map();
let dockerRuntimeActionsApi = null;
const DOCKER_RUNTIME_WIDTH_PHASES = Object.freeze({
idle: 'idle',
@@ -871,11 +881,16 @@ const getDockerRuntimeHierarchyApi = () => {
applyFolderPreviewLayout: ($preview, settings) => applyFolderPreviewLayout($preview, settings),
layoutFolderPreviewRows: ($preview, settings) => layoutFolderPreviewRows($preview, settings),
previewModelModule: folderPreviewModelModule,
+ childFolderPreviewMenuModule: dockerChildFolderPreviewMenuModule,
buildDockerPreviewItem: (options) => buildDockerPreviewItem(options),
appendDockerPreviewActionButtons: ($target, settings, containerName, shellValue, webuiUrl, options = {}) =>
appendDockerPreviewActionButtons($target, settings, containerName, shellValue, webuiUrl, options),
decorateDockerPreviewMemberTriggers: ($targets, folderId, containerName) =>
decorateDockerPreviewMemberTriggers($targets, folderId, containerName),
+ bindDockerNestedPreviewContext: ($item, $tooltipTrigger, entry, folder, folderId) =>
+ bindDockerNestedPreviewContext($item, $tooltipTrigger, entry, folder, folderId),
+ auditDockerPreviewContextBridges: ($preview, settings) =>
+ getDockerPreviewActionsApi()?.auditDockerPreviewContextBridges?.($preview, settings),
getSafeWebuiUrl: (value) => getSafeWebuiUrl(value),
isCompactMultiRowPreview: (settings) => isCompactMultiRowPreview(settings),
editFolder: (id) => editFolder(id),
@@ -898,6 +913,9 @@ const getDockerRuntimeHierarchyApi = () => {
}
addDockerFolderContext(id);
},
+ recordChildFolderPreviewRender: () => getDockerPreviewActionsApi()?.recordChildFolderPreviewRender?.(),
+ recordChildFolderPreviewBinding: () => getDockerPreviewActionsApi()?.recordChildFolderPreviewBinding?.(),
+ recordChildFolderPreviewMenuOpen: (details) => getDockerPreviewActionsApi()?.recordChildFolderPreviewMenuOpen?.(details),
debugEnabled: FOLDER_VIEW_DEBUG_MODE,
console: window.console
});
@@ -936,6 +954,7 @@ const getDockerRuntimeActionsApi = () => {
runDockerGuardedAction: (actionName, action, context = {}) =>
runDockerGuardedAction(actionName, action, context),
getDockerMenuLabel: (key, fallback) => getDockerMenuLabel(key, fallback),
+ webuiProfiles: folderWebuiProfilesModule,
folderEvents,
refreshDockerRuntimeState: (options = {}) => refreshDockerRuntimeStateInPlace(options),
queueLoadlistRefresh: (options = {}) => queueLoadlistRefresh(options),
@@ -1014,13 +1033,13 @@ const buildDockerIsolatedViewDeps = () => ({
readPinnedFolderIds: (prefs = {}) => Array.isArray(prefs?.pinnedFolderIds) ? prefs.pinnedFolderIds : [],
isFolderLocked: (folderId) => isDockerFolderLocked(folderId),
createFolderBtn: () => createFolderBtn(),
- editFolder: (id) => editFolder(id),
+ editFolder: (id, options = {}) => editFolder(id, options),
actionFolder: (id, action, options = {}) => actionFolder(id, action, options),
updateFolder: (id, options = {}) => updateFolder(id, options),
forceUpdateFolder: (id, options = {}) => forceUpdateFolder(id, options),
getSafeWebuiUrl: (value) => getSafeWebuiUrl(value),
- openFolderWebuisFromMenu: (id, runningOnly = true, includeDescendants = false) =>
- openFolderWebuisFromMenu(id, runningOnly, includeDescendants),
+ openFolderWebuisFromMenu: (id, runningOnly = true, includeDescendants = false, profileId = '') =>
+ openFolderWebuisFromMenu(id, runningOnly, includeDescendants, profileId),
openWebuiInNewTab: (url) => openWebuiInNewTab(url),
openWebuiPopupWindow: (url, targetName = '_blank') => openWebuiPopupWindow(url, targetName),
openTerminal: (type, containerName, shellValue) => openTerminal(type, containerName, shellValue),
@@ -1406,6 +1425,13 @@ const buildDockerPreviewItem = ({ entry = {}, settings = {}, autostart = false }
const textWidth = String(settings?.preview_text_width || '').trim();
const textWidthData = textWidth ? ` data-fv-preview-text-width="${escapeHtml(textWidth)}"` : '';
const autostartClass = autostart ? ' autostart' : '';
+ const decoratePreviewIdentity = ($item) => {
+ const containerId = String(entry?.shortId || entry?.id || entry?.info?.Id || '').replace(/^sha256:/i, '').trim();
+ const containerName = String(entry?.name || entry?.info?.Name || '').trim();
+ if (containerId) $item.attr('data-fv-container-id', containerId);
+ if (containerName) $item.attr('data-fv-container-name', containerName);
+ return $item;
+ };
let itemMarkup = '';
let triggerSelector;
@@ -1452,7 +1478,7 @@ const buildDockerPreviewItem = ({ entry = {}, settings = {}, autostart = false }
triggerSelector = '.fv-docker-preview-card';
break;
}
- const $compactItem = $(itemMarkup);
+ const $compactItem = decoratePreviewIdentity($(itemMarkup));
return {
$item: $compactItem,
$tooltipTrigger: triggerSelector === '.fv-docker-preview-card'
@@ -1505,7 +1531,7 @@ const buildDockerPreviewItem = ({ entry = {}, settings = {}, autostart = false }
break;
}
- const $item = $(itemMarkup);
+ const $item = decoratePreviewIdentity($(itemMarkup));
return {
$item,
$tooltipTrigger: $item.find(triggerSelector).first()
@@ -1967,6 +1993,18 @@ const initializeDockerTooltipOnDemand = ($target, init, hoverOpen = true) => {
// default preview rendering stays lightweight until the user interacts.
const DOCKER_PREVIEW_POPUP_ENABLED = true;
+const bindDockerNestedPreviewContext = ($item, $tooltipTrigger, entry = {}, folder = {}, folderId = '') => {
+ return getDockerPreviewActionsApi().bindDockerNestedPreviewContext({
+ $item, $tooltipTrigger, entry, settings: folder?.settings || {},
+ bindAdvancedContext: ($trigger, containerName) => {
+ const bindAdvancedContext = dockerAdvancedPreviewContextBindersByName.get(containerName);
+ return typeof bindAdvancedContext === 'function'
+ ? bindAdvancedContext($trigger, { folder, folderId })
+ : false;
+ }
+ });
+};
+
const getPrefsOrderedFolderMap = (folders, prefs) => {
const source = folders && typeof folders === 'object' ? folders : {};
if (typeof utils.orderFoldersByPrefs === 'function') {
@@ -2092,6 +2130,16 @@ const isDockerFolderPinned = (folderId) => {
const runtimePinned = normalizeDockerPinnedFolderIdList(dockerRuntimeStateStore.get('pinnedFolderIds', []));
return runtimePinned.includes(id);
};
+const dockerHiddenFoldersModule = window.FolderViewPlusFoundationModules?.dockerHiddenFolders; const dockerHiddenFoldersApi = dockerHiddenFoldersModule.createApi({
+ window, document, $, runtimeStateStore: dockerRuntimeStateStore, safeActionRunner: dockerSafeUiActionRunner,
+ getFolders: () => globalFolders || {}, getPrefs: () => folderTypePrefs || {}, setPrefs: (prefs) => { folderTypePrefs = prefs; },
+ normalizePrefs: (prefs) => utils.normalizePrefs(prefs || {}), normalizeParentId: (id) => normalizeFolderParentId(id), readFolderIdFromRow: (row) => readFolderIdFromRow(row), readFolderOwnerFromRow: (row) => readFolderOwnerFromRow(row),
+ runGuardedAction: (name, action, context) => runDockerGuardedAction(name, action, context), savePrefs: (patch, currentPrefs) => saveDockerRuntimeToolbarPrefs(patch, currentPrefs), fetchPrefs: () => fetchDockerPinnedFolderPrefs(),
+ syncDependentUi: () => { applyDockerFocusedFolderState(); applyDockerRuntimeToolbarFilterState(); renderDockerRuntimeActionBar(resolveDockerPageViewMode()); refreshDockerRuntimeSortableRows(); queueDockerRuntimeResizerBind(); scheduleDockerRuntimeWidthReflow('folder-visibility', 24); },
+ getFocusedFolderId: () => dockerFocusedFolderId, clearFocusedFolder: () => { dockerRuntimeStateStore.set({ focusedFolderId: '' }); dockerFocusedFolderId = ''; },
+ offerUndo: (payload) => dockerRuntimeActionBarApi?.offerHiddenFolderUndo?.(payload), dismissUndo: (folderId = '') => dockerRuntimeActionBarApi?.clearHiddenFolderUndo?.(folderId),
+ translate: (key, fallback) => dockerT(key, fallback), escapeHtml, svgIcon: (name, options = {}) => window.FolderViewPlusUI?.svgIcon?.(name, options) || ''
+});
const readFolderIdFromRow = (row) => {
if (!row || !row.className) {
return '';
@@ -3639,7 +3687,7 @@ const restoreDockerNativeHostList = async (requestBundle = null) => {
'folder-element',
'fv-nested-hidden',
'fv-folder-focus-hidden',
- 'fv-toolbar-filter-hidden'
+ 'fv-toolbar-filter-hidden', 'fv-folder-user-hidden', 'fv-folder-hidden-revealed'
);
row.classList.add('sortable');
row.style.removeProperty('display');
@@ -3794,6 +3842,7 @@ if (dockerRuntimeActionBarModule && typeof dockerRuntimeActionBarModule.createAp
dockerFocusedFolderId = '';
},
scheduleWidthReflow: (reason, delayMs) => scheduleDockerRuntimeWidthReflow(reason, delayMs),
+ hiddenFolders: dockerHiddenFoldersApi,
buildFolderHierarchy,
expandFolderBranch,
collapseFolderBranch,
@@ -3859,6 +3908,7 @@ const syncDockerVisibleFoldersFromRuntimeCache = (changedNames = null) => {
renderRuntimeHealthBadge(globalFolders, folderTypePrefs);
refreshDockerFolderQuickActionStates();
applyDockerFocusedFolderState();
+ dockerHiddenFoldersApi.applyVisibility();
applyDockerRuntimeToolbarFilterState();
renderDockerRuntimeActionBar(resolveDockerPageViewMode());
refreshDockerPreviewTooltipContent(changedSet);
@@ -4605,6 +4655,7 @@ let createFoldersQueued = false;
*/
const createFolders = async () => {
dockerDeferredPreviewController.flush();
+ dockerAdvancedPreviewContextBindersByName.clear();
const dockerRuntimeRoot = document.querySelector('#docker_list, #docker_view');
dockerRuntimePerformanceTelemetry?.observe?.(dockerRuntimeRoot);
dockerRuntimePerformanceTelemetry?.mark?.('nativeRowsVisible', {
@@ -4871,6 +4922,7 @@ const createFolders = async () => {
renderRuntimeHealthBadge(globalFolders, folderTypePrefs);
refreshDockerFolderQuickActionStates();
applyDockerFocusedFolderState();
+ dockerHiddenFoldersApi.applyVisibility();
applyDockerRuntimeToolbarFilterState();
renderDockerRuntimeActionBar(resolveDockerPageViewMode());
runDockerRuntimeWidthReflow('pre-visible-folder-commit', {
@@ -5133,6 +5185,9 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
autostart
});
const { $item, $tooltipTrigger } = builtPreview;
+ $item
+ .attr('data-fv-container-id', String(ctid || '').trim())
+ .attr('data-fv-container-name', String(previewEntry?.name || '').trim());
$createdFolderPreview.append($item);
if (folder.settings.context === 1) {
getDockerPreviewActionsApi().bindDockerPreviewDefaultContextBridge($item, $sourceRow, folder.settings);
@@ -5153,7 +5208,7 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
const resilientSingleRowPreview = (previewMode, selector, folderTrId, ctid, autostart, previewEntry, $sourceRow) =>
getDockerPreviewActionsApi().renderDockerSingleRowPreview({
$sourceRow, $preview: $createdFolderPreview, selector, previewMode,
- context: folder.settings.context, ctid, autostart,
+ context: folder.settings.context, ctid, containerName: previewEntry?.name, autostart,
appendModelPreview: () => appendCompactPreview(
folderTrId, ctid, autostart, previewEntry, $sourceRow,
{ preferNativeDefaultContext: false }
@@ -5174,7 +5229,12 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
if (compactMultiRowPreview) {
return appendCompactPreview(folderTrId, ctid, autostart, previewEntry, $sourceRow);
}
- $(`tr.folder-id-${folderTrId} div.folder-preview`).append($(`tr.folder-id-${folderTrId} div.folder-storage > tr > td.ct-name > span.outer > span.hand:last`).clone().addClass(`${autostart ? 'autostart' : ''}`));
+ const $previewItem = $(`tr.folder-id-${folderTrId} div.folder-storage > tr > td.ct-name > span.outer > span.hand:last`)
+ .clone()
+ .addClass(`${autostart ? 'autostart' : ''}`)
+ .attr('data-fv-container-id', String(ctid || '').trim())
+ .attr('data-fv-container-name', String(previewEntry?.name || '').trim());
+ $(`tr.folder-id-${folderTrId} div.folder-preview`).append($previewItem);
if(folder.settings.context === 2 || folder.settings.context === 0) {
let tmpId = $(`tr.folder-id-${folderTrId} div.folder-preview > span.hand:last`);
tmpId.attr("id", "folder-preview-" + ctid);
@@ -5202,7 +5262,10 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
lstSpan = $(`tr.folder-id-${folderTrId} div.folder-preview > span.outer:last`);
}
lstSpan.append($(''));
- lstSpan.children('span.inner:last').append($(`tr.folder-id-${folderTrId} div.folder-storage > tr > td.ct-name > span.outer > span.inner > span.appname:last`).clone().addClass(`${autostart ? 'autostart' : ''}`));
+ const $previewItem = lstSpan.children('span.inner:last')
+ .attr('data-fv-container-id', String(ctid || '').trim())
+ .attr('data-fv-container-name', String(previewEntry?.name || '').trim());
+ $previewItem.append($(`tr.folder-id-${folderTrId} div.folder-storage > tr > td.ct-name > span.outer > span.inner > span.appname:last`).clone().addClass(`${autostart ? 'autostart' : ''}`));
if(folder.settings.context === 2 || folder.settings.context === 0) {
let tmpId = $(`tr.folder-id-${folderTrId} div.folder-preview span.inner:last > span.appname > a.exec`);
tmpId.attr("id", "folder-preview-" + ctid);
@@ -5378,9 +5441,14 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
$(`tr.folder-id-${id} div.folder-preview span.inner > span.appname`).css("width", folder.settings.preview_text_width || '');
if (FOLDER_VIEW_DEBUG_MODE && folder.settings.preview_text_width) console.log(`[FV3_DEBUG] createFolder (id: ${id}): Set preview text width to ${folder.settings.preview_text_width}.`);
- if(DOCKER_PREVIEW_POPUP_ENABLED && tooltip_trigger_element && tooltip_trigger_element.length > 0) {
- if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] createFolder (id: ${id}), container ${ct.shortId}: tooltip_trigger_element is valid. Deferring tooltipster initialization until first interaction.`);
- const triggerMode = folder.settings.context_trigger === 1 && !FOLDER_VIEW_TOUCH_MODE ? 'hover' : 'click';
+ const bindAdvancedPreviewContext = ($contextTrigger, context = {}) => {
+ const contextFolder = context.folder && typeof context.folder === 'object' ? context.folder : folder;
+ const contextFolderId = String(context.folderId || id).trim() || id;
+ const tooltipTarget = $contextTrigger && $contextTrigger.length ? $contextTrigger : $();
+ if(DOCKER_PREVIEW_POPUP_ENABLED && tooltipTarget.length > 0) {
+ if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] createFolder (id: ${contextFolderId}), container ${ct.shortId}: tooltip target is valid. Deferring tooltipster initialization until first interaction.`);
+ const triggerMode = contextFolder.settings.context_trigger === 1 && !FOLDER_VIEW_TOUCH_MODE ? 'hover' : 'click';
+ const tooltip_trigger_element = tooltipTarget;
initializeDockerTooltipOnDemand($(tooltip_trigger_element), () => $(tooltip_trigger_element).tooltipster({
interactive: true,
theme: ['tooltipster-docker-folder'],
@@ -5400,14 +5468,14 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
if (FOLDER_VIEW_DEBUG_MODE) {
console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): functionBefore. Instance:`, instance, "Helper:", helper, "Origin:", origin);
- console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Current folder settings for context:`, {...folder.settings});
+ console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Current folder settings for context:`, {...contextFolder.settings});
}
// Dispatch your custom event
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Dispatching docker-tooltip-before event.`);
folderEvents.dispatchEvent(new CustomEvent('docker-tooltip-before', {detail: {
- folder: folder,
- id: id, // Folder ID
+ folder: contextFolder,
+ id: contextFolderId, // Folder ID
containerInfo: ct, // Container info
origin: origin,
charts: charts,
@@ -5431,8 +5499,8 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Dispatching docker-tooltip-ready-start event.`);
folderEvents.dispatchEvent(new CustomEvent('docker-tooltip-ready-start', {detail: {
- folder: folder,
- id: id,
+ folder: contextFolder,
+ id: contextFolderId,
containerInfo: ct,
origin: triggerOriginEl,
tooltip: tooltipDomEl,
@@ -5450,7 +5518,7 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
x: {
type: 'realtime',
realtime: {
- duration: 1000*(folder.settings.context_graph_time || 60),
+ duration: 1000*(contextFolder.settings.context_graph_time || 60),
refresh: 1000,
delay: 1000
},
@@ -5483,10 +5551,10 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
}
}
};
- if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Chart.js options:`, options, "Graph mode setting:", folder.settings.context_graph);
+ if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Chart.js options:`, options, "Graph mode setting:", contextFolder.settings.context_graph);
charts = [];
- switch (folder.settings.context_graph) {
+ switch (contextFolder.settings.context_graph) {
case 0:
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Graph mode 0 (None).`);
diabled = [0, 1, 2];
@@ -5609,8 +5677,8 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Dispatching docker-tooltip-ready-end event.`);
folderEvents.dispatchEvent(new CustomEvent('docker-tooltip-ready-end', {detail: {
- folder: folder,
- id: id,
+ folder: contextFolder,
+ id: contextFolderId,
containerInfo: ct,
origin: triggerOriginEl,
tooltip: tooltipDomEl,
@@ -5627,8 +5695,8 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): functionAfter. Instance:`, instance, "Helper:", helper, "Origin:", origin);
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] Tooltipster (ct: ${ct.shortId}): Dispatching docker-tooltip-after event.`);
folderEvents.dispatchEvent(new CustomEvent('docker-tooltip-after', {detail: {
- folder: folder,
- id: id,
+ folder: contextFolder,
+ id: contextFolderId,
containerInfo: ct,
origin: origin,
charts,
@@ -5660,11 +5728,20 @@ const createFolder = (folder, id, positionInMainOrder, liveOrderArray, container
},
content: $('Loading preview...
')
}), triggerMode === 'hover');
- } else if (FOLDER_VIEW_DEBUG_MODE && tooltip_trigger_element && tooltip_trigger_element.length > 0) {
- console.log(`[FV3_DEBUG] createFolder (id: ${id}), container ${ct.shortId}: FolderView preview popup runtime is disabled; skipping tooltip initialization.`);
- } else {
- if (FOLDER_VIEW_DEBUG_MODE) console.warn(`[FV3_DEBUG] createFolder (id: ${id}), container ${ct.shortId}: tooltip_trigger_element is NOT valid. Tooltipster NOT initialized. This is likely the problem if folder.settings.context === 2.`);
- }
+ return true;
+ }
+ if (FOLDER_VIEW_DEBUG_MODE && tooltipTarget.length > 0) {
+ console.log(`[FV3_DEBUG] createFolder (id: ${contextFolderId}), container ${ct.shortId}: FolderView preview popup runtime is disabled; skipping tooltip initialization.`);
+ } else if (FOLDER_VIEW_DEBUG_MODE) {
+ console.warn(`[FV3_DEBUG] createFolder (id: ${contextFolderId}), container ${ct.shortId}: tooltip target is not valid. Tooltipster was not initialized.`);
+ }
+ return false;
+ };
+ dockerAdvancedPreviewContextBindersByName.set(
+ String(container_name_in_folder || '').trim(),
+ bindAdvancedPreviewContext
+ );
+ bindAdvancedPreviewContext($(tooltip_trigger_element));
const elementForPreviewOpts = $(`tr.folder-id-${id} div.folder-preview > span:last`); // Re-check if this is always correct
if (FOLDER_VIEW_DEBUG_MODE) console.log(`[FV3_DEBUG] createFolder (id: ${id}), container ${container_name_in_folder}: Preview element for options:`, elementForPreviewOpts[0]);
@@ -6154,12 +6231,7 @@ const buildDockerFolderEditorUrl = (id = '', options = {}) => {
? actionsApi.buildDockerFolderEditorUrl(id, options)
: `/Docker/Folder?type=docker&_=${String(Date.now())}#type=docker`;
};
-const editFolder = (id) => {
- const actionsApi = getDockerRuntimeActionsApi();
- if (actionsApi && typeof actionsApi.editFolder === 'function') {
- actionsApi.editFolder(id);
- }
-};
+const editFolder = (id, options = {}) => getDockerRuntimeActionsApi()?.editFolder?.(id, options);
const createChildFolder = (id) => {
const actionsApi = getDockerRuntimeActionsApi();
if (actionsApi && typeof actionsApi.createChildFolder === 'function') {
@@ -6218,12 +6290,8 @@ const collectFolderWebuiTargets = (id, includeDescendants = true, runningOnly =
: [];
};
-const openFolderWebuisFromMenu = (id, runningOnly = true, includeDescendants = false) => {
- const actionsApi = getDockerRuntimeActionsApi();
- if (actionsApi && typeof actionsApi.openFolderWebuisFromMenu === 'function') {
- actionsApi.openFolderWebuisFromMenu(id, runningOnly, includeDescendants);
- }
-};
+const openFolderWebuisFromMenu = (id, runningOnly = true, includeDescendants = false, profileId = '') =>
+ getDockerRuntimeActionsApi()?.openFolderWebuisFromMenu?.(id, runningOnly, includeDescendants, profileId);
const copyDockerFolderSettingsFromMenu = async (id) => {
const actionsApi = getDockerRuntimeActionsApi();
@@ -6412,19 +6480,20 @@ const DOCKER_CONTEXT_QUICK_ACTION_LABELS = new Set([
'pin folder',
'unpin folder',
'lock folder',
- 'unlock folder'
+ 'unlock folder', ...dockerHiddenFoldersModule.QUICK_LABELS
]);
const dockerContextQuickStripAdapter = createDockerContextMenuQuickStripAdapter({
menuClassName: 'fvplus-docker-context-menu',
quickItemClassName: 'fvplus-docker-quick-item',
clearClassName: 'fvplus-docker-quick-clear',
labelSet: DOCKER_CONTEXT_QUICK_ACTION_LABELS,
+ minimumItems: 4, maximumItems: 4,
iconClassCandidates: [
'fa-bullseye',
'fa-dot-circle-o',
'fa-thumb-tack',
'fa-lock',
- 'fa-unlock-alt'
+ 'fa-unlock-alt', ...dockerHiddenFoldersModule.QUICK_ICON_CLASSES
]
});
const DOCKER_CONTEXT_MENU_SELECTORS = [
@@ -6439,6 +6508,7 @@ const queueDockerFolderContextQuickIcons = (attempt = 0) => {
return;
}
dockerContextQuickStripAdapter.queueEnhance(attempt);
+ window.setTimeout(() => dockerHiddenFoldersApi?.decorateQuickIcon?.(DOCKER_CONTEXT_MENU_SELECTORS, attempt), 24);
};
const getVisibleDockerContextMenus = () => {
const jq = window.jQuery || window.$;
@@ -6721,6 +6791,7 @@ const addDockerFolderContext = (id) => {
toggleDockerFolderLock(id);
}
});
+ opts.push(dockerHiddenFoldersApi.buildQuickAction(id));
appendDivider();
@@ -6899,6 +6970,11 @@ const addDockerFolderContext = (id) => {
appendDivider();
}
+ if (folderWebuiProfilesModule?.appendRuntimeMenuItems?.(opts, {
+ profiles: folderData.settings.webui_profiles, runtimeEntries: getScopedRuntimeContainersForFolder(id, false), getSafeWebuiUrl, translate: dockerT,
+ onOpen: (profileId) => openFolderWebuisFromMenu(id, true, false, profileId), onManage: () => editFolder(id, { section: 'webuiProfiles' })
+ })) appendDivider();
+
opts.push({
text: $.i18n('edit'),
icon: 'fa-wrench',
@@ -7666,6 +7742,7 @@ const applyRuntimePrefs = (prefs) => {
queueDockerRuntimePrivacyToggleMount();
renderRuntimeHealthBadge(globalFolders, normalized);
scheduleLiveRefresh(normalized);
+ dockerHiddenFoldersApi.applyVisibility();
};
const bindDockerRuntimePreferenceSync = () => {
@@ -7676,9 +7753,9 @@ const bindDockerRuntimePreferenceSync = () => {
if (snapshot?.type !== 'docker' || !snapshot?.prefs) {
return;
}
- const nextPrefs = applyDockerPinnedFolderPrefsOverride(utils.normalizePrefs(snapshot.prefs));
+ const nextPrefs = dockerHiddenFoldersApi.reconcilePrefs(applyDockerPinnedFolderPrefsOverride(utils.normalizePrefs(snapshot.prefs)));
folderTypePrefs = nextPrefs;
- applyRuntimePrefs(nextPrefs);
+ applyRuntimePrefs(nextPrefs); applyDockerRuntimeToolbarFilterState(); renderDockerRuntimeActionBar(resolveDockerPageViewMode(nextPrefs));
});
};
bindDockerRuntimePreferenceSync();
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js
index acedb4be..b2152f95 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js
@@ -53,6 +53,17 @@
const collapseFolderBranch = typeof deps.collapseFolderBranch === 'function' ? deps.collapseFolderBranch : (() => {});
const createFolder = typeof deps.createFolder === 'function' ? deps.createFolder : (() => {});
const showError = typeof deps.showError === 'function' ? deps.showError : (() => {});
+ const hiddenFolders = deps.hiddenFolders || {};
+ const getHiddenVisibilitySummary = typeof hiddenFolders.getSummary === 'function'
+ ? hiddenFolders.getSummary
+ : (() => ({ explicitCount: 0, effectiveCount: 0, totalFolderCount: 0, revealHiddenFolders: false }));
+ const applyHiddenFolderState = typeof hiddenFolders.applyVisibility === 'function' ? hiddenFolders.applyVisibility : (() => {});
+ const setRevealHiddenFolders = typeof hiddenFolders.setReveal === 'function' ? hiddenFolders.setReveal : (() => false);
+ const restoreHiddenFolder = typeof hiddenFolders.restoreFolder === 'function' ? hiddenFolders.restoreFolder : (async () => false);
+ const restoreAllHiddenFolders = typeof hiddenFolders.restoreAll === 'function' ? hiddenFolders.restoreAll : (async () => false);
+ const svgIcon = typeof hiddenFolders.svgIcon === 'function'
+ ? hiddenFolders.svgIcon
+ : ((name, options = {}) => ``);
const translate = typeof deps.translate === 'function'
? deps.translate
: ((key, fallback = '') => win?.FolderViewPlusI18n?.t?.(key, fallback) || fallback || key);
@@ -66,6 +77,8 @@
let eventsBound = false;
let busy = false;
let menuPositionFrame = 0;
+ let hiddenFolderUndo = null;
+ let hiddenFolderUndoTimer = 0;
const getListRows = () => hostAdapter && typeof hostAdapter.queryRows === 'function'
? hostAdapter.queryRows('all')
@@ -175,6 +188,7 @@
});
}
applyFocusedFolderState();
+ applyHiddenFolderState();
scheduleWidthReflow('toolbar-filter', 24);
};
@@ -186,13 +200,31 @@
${count === null ? '' : `${Number(count) || 0}`}
`;
- const buildViewMenuHtml = (currentMode) => VIEW_OPTIONS.map((option) => `
+ const buildViewMenuHtml = (currentMode) => {
+ const hiddenState = getHiddenVisibilitySummary();
+ const hiddenCount = Math.max(0, Number(hiddenState?.explicitCount) || 0);
+ const revealHidden = hiddenState?.revealHiddenFolders === true;
+ const modeOptions = VIEW_OPTIONS.map((option) => `
`).join('');
+ const hiddenLabel = `${translate('docker.visibility.hidden-folders', 'Hidden folders')} (${hiddenCount})`;
+ return `${modeOptions}
+
+
+ `;
+ };
const buildToolsMenuHtml = () => {
const hideEmpty = utils.normalizePrefs(getPrefs() || {}).hideEmptyFolders === true;
@@ -259,6 +291,12 @@
const hideEmpty = utils.normalizePrefs(getPrefs() || {}).hideEmptyFolders === true;
if (reconcileFilterWithPrefs()) applyFilterState();
const state = summarize();
+ const hiddenState = getHiddenVisibilitySummary();
+ const allFoldersHidden = isFolderView
+ && hiddenState?.revealHiddenFolders !== true
+ && Number(hiddenState?.totalFolderCount || 0) > 0
+ && Number(hiddenState?.effectiveCount || 0) >= Number(hiddenState?.totalFolderCount || 0);
+ const undoActive = !!hiddenFolderUndo && Number(hiddenFolderUndo.expiresAt || 0) > Date.now();
const folderControls = isFolderView ? `
${buildActionButtonHtml({ action: 'add-folder', label: translate('docker.actions.add-folder', 'Add Folder'), icon: 'fa-plus' })}
${buildActionButtonHtml({ action: 'expand-all', label: translate('docker.actions.expand-all', 'Expand All'), icon: 'fa-expand' })}
@@ -287,7 +325,18 @@
-
`;
+
+ ${undoActive ? `
+ ${svgIcon('eye-off', { className: 'fvplus-docker-visibility-notice-icon' })}
+ ${escapeHtml(translate('docker.visibility.hidden-title', 'Folder hidden'))} ${escapeHtml(String(hiddenFolderUndo.folderName || translate('docker.visibility.folder-fallback', 'Folder')))} ${escapeHtml(translate('docker.visibility.hidden-message', 'was hidden from the Docker view.'))}
+
+
` : ''}
+ ${allFoldersHidden ? `
+ ${svgIcon('eye-off', { className: 'fvplus-docker-visibility-notice-icon' })}
+ ${escapeHtml(translate('docker.visibility.all-hidden-title', 'All folder rows are hidden.'))} ${escapeHtml(translate('docker.visibility.all-hidden-help', 'Reveal them temporarily or restore every hidden folder.'))}
+
+
+
` : ''}`;
Array.from(bar.querySelectorAll('button')).forEach((button) => {
button.disabled = button.disabled || busy;
});
@@ -408,10 +457,39 @@
actionMenuOpen = '';
folderFilterMode = 'all';
clearFocusedFolder();
+ setRevealHiddenFolders(false);
applyFilterState();
sync();
};
+ const clearHiddenFolderUndo = (folderId = '') => {
+ const expectedId = String(folderId || '').trim();
+ if (expectedId && String(hiddenFolderUndo?.folderId || '') !== expectedId) return false;
+ if (hiddenFolderUndoTimer) {
+ win?.clearTimeout?.(hiddenFolderUndoTimer);
+ hiddenFolderUndoTimer = 0;
+ }
+ hiddenFolderUndo = null;
+ return true;
+ };
+
+ const offerHiddenFolderUndo = ({ folderId = '', folderName = '' } = {}) => {
+ clearHiddenFolderUndo();
+ const id = String(folderId || '').trim();
+ if (!id) return;
+ hiddenFolderUndo = {
+ folderId: id,
+ folderName: String(folderName || translate('docker.visibility.folder-fallback', 'Folder')),
+ expiresAt: Date.now() + 10000
+ };
+ hiddenFolderUndoTimer = win?.setTimeout?.(() => {
+ hiddenFolderUndoTimer = 0;
+ hiddenFolderUndo = null;
+ sync();
+ }, 10000) || 0;
+ sync();
+ };
+
const getRootFolderIds = () => {
const folders = getFolders() || {};
const hierarchy = buildFolderHierarchy(folders);
@@ -443,6 +521,37 @@
if (SETTINGS_ROUTES[route] && win?.location) win.location.href = SETTINGS_ROUTES[route];
return;
}
+ const hiddenButton = target.closest?.('[data-fvplus-docker-hidden]');
+ if (hiddenButton && !hiddenButton.disabled) {
+ event.preventDefault();
+ const action = String(hiddenButton.getAttribute('data-fvplus-docker-hidden') || '');
+ actionMenuOpen = '';
+ if (action === 'toggle-reveal') {
+ folderFilterMode = 'all';
+ clearFocusedFolder();
+ const current = getHiddenVisibilitySummary()?.revealHiddenFolders === true;
+ setRevealHiddenFolders(!current);
+ applyFilterState();
+ sync();
+ }
+ if (action === 'restore-all') {
+ runTask(async () => {
+ await restoreAllHiddenFolders();
+ clearHiddenFolderUndo();
+ setRevealHiddenFolders(false);
+ applyFilterState();
+ });
+ }
+ if (action === 'undo' && hiddenFolderUndo?.folderId) {
+ const undoId = hiddenFolderUndo.folderId;
+ clearHiddenFolderUndo(undoId);
+ runTask(async () => {
+ await restoreHiddenFolder(undoId);
+ applyFilterState();
+ });
+ }
+ return;
+ }
const toolButton = target.closest?.('[data-fvplus-docker-tool]');
if (toolButton && !toolButton.disabled) {
event.preventDefault();
@@ -540,7 +649,9 @@
setFilterMode: setFolderFilter,
setPageViewMode,
resetView,
- getFilterMode: () => folderFilterMode
+ getFilterMode: () => folderFilterMode,
+ offerHiddenFolderUndo,
+ clearHiddenFolderUndo
});
};
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js
index 1fd43702..fa1cfce6 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js
@@ -61,7 +61,7 @@
const getDockerMenuLabel = typeof deps.getDockerMenuLabel === 'function'
? deps.getDockerMenuLabel
: ((_key, fallback) => String(fallback || _key || '').trim());
- const requestClient = deps.requestClient || win?.FolderViewPlusRequest || null;
+ const webuiProfiles = deps.webuiProfiles || win?.FolderViewPlusFoundationModules?.folderWebuiProfiles || null, requestClient = deps.requestClient || win?.FolderViewPlusRequest || null;
const folderSettingsTransfer = deps.folderSettingsTransfer
|| (typeof win?.FolderViewPlusFolderSettingsTransfer?.createApi === 'function'
? win.FolderViewPlusFolderSettingsTransfer.createApi({ window: win })
@@ -518,7 +518,6 @@
// Editor prefill is best-effort only.
}
};
-
const buildDockerFolderEditorUrl = (id = '', options = {}) => {
const params = new URLSearchParams();
const hashParams = new URLSearchParams();
@@ -533,23 +532,23 @@
params.set('parentId', parentId);
hashParams.set('parentId', parentId);
}
+ const section = String(options?.section || '').trim();
+ if (section === 'webuiProfiles') { params.set('section', section); hashParams.set('section', section); }
params.set('_', String(Date.now()));
return `/Docker/Folder?${params.toString()}#${hashParams.toString()}`;
};
-
- const editFolder = (id) => {
+ const editFolder = (id, options = {}) => {
if (!ensureDockerFolderUnlocked(id, 'Edit folder')) {
return;
}
debugLog(`[FV3_DEBUG] editFolder (id: ${id}): Redirecting to edit page.`);
seedFolderEditorPrefill('docker', id);
- const targetUrl = buildDockerFolderEditorUrl(id);
+ const targetUrl = buildDockerFolderEditorUrl(id, options);
recordFolderEditorLaunchDebug('docker', 'docker', id, targetUrl);
if (win?.location) {
win.location.href = targetUrl;
}
};
-
const createChildFolder = (parentId) => {
const safeParentId = normalizeFolderParentId(parentId);
if (!safeParentId || !getFolderById(safeParentId)) {
@@ -617,7 +616,7 @@
return out;
}, []);
- const showFolderWebuiPopupWarning = (openedCount, totalCount, blockedUrls) => {
+ const showFolderWebuiPopupWarning = (openedCount, totalCount, blockedUrls, retryLabel = 'Open all WebUIs') => {
const blockedList = Array.isArray(blockedUrls) ? blockedUrls.slice(0, 6) : [];
const linkHtml = blockedList
.map((url) => ``)
@@ -644,7 +643,7 @@
``,
`- Click the popup-blocked icon in your browser address bar.
`,
`- Choose to always allow popups/redirects for this Unraid host.
`,
- `- Run Open all WebUIs again.
`,
+ `- Run ${escapeHtml(retryLabel)} again.
`,
`
`,
``,
`
@@ -538,6 +537,7 @@
findBasicByFieldName(form, 'status_color_started'),
findBasicByFieldName(form, 'status_warn_stopped_percent')
],
+ webuiProfiles: pageType === 'docker' ? [form.querySelector('#fvWebuiProfilesSection')] : [],
rules: [
findBasicByFieldName(form, 'regex'),
root.document.getElementById('fvFolderAutoRulesPanel')
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.preview.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.preview.js
index edff31d1..84201633 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.preview.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.preview.js
@@ -113,6 +113,7 @@
const safeHoverAnimation = ['lift', 'bounce', 'pop', 'glow', 'flip', 'wiggle'].includes(hoverAnimationToken) ? hoverAnimationToken : 'none';
const accentEnabled = isFolderAccentEnabled({ folder_accent_enabled: form.folder_accent_enabled?.checked === true });
const accentColor = normalizeHexColor(form.folder_accent_color?.value, deps.defaultFolderAccentColor || '#ffca63');
+ const statusTextColor = normalizeHexColor(form.status_color_text?.value, deps.defaultFolderStatusColors?.text || '#ffffff');
const icon = String(form.icon?.value || '').trim() || deps.defaultFolderIconPath || '';
const name = String(form.name?.value || '').trim() || 'Unnamed folder';
const hideNestedPreviewItems = form.preview_hide_nested_items?.checked === true;
@@ -233,9 +234,12 @@
}
const statusNode = memberNode.querySelector('.fv-live-member-status');
if (statusNode instanceof HTMLElement) {
- statusNode.style.color = safeStateColor;
+ statusNode.style.color = statusNode.classList.contains('is-symbol') ? safeStateColor : statusTextColor;
}
});
+ canvas.find('.fv-live-member-status:not(.is-symbol)').each((_, statusNode) => {
+ if (statusNode instanceof HTMLElement) statusNode.style.color = statusTextColor;
+ });
};
const updateLiveSummary = () => {
@@ -280,9 +284,10 @@
? 'Preview disabled'
: `${previewLabel} - ${normalizePreviewRowLimit(form.preview_rows?.value) === 0 ? 'Unlimited rows' : `${normalizePreviewRowLimit(form.preview_rows?.value)} row${normalizePreviewRowLimit(form.preview_rows?.value) === 1 ? '' : 's'}`}`
);
- $('#fvSwatchStarted').css('background-color', normalizeHexColor(form.status_color_started?.value, deps.defaultFolderStatusColors?.started || '#ffffff'));
+ $('#fvSwatchStarted').css('background-color', normalizeHexColor(form.status_color_started?.value, deps.defaultFolderStatusColors?.started || '#55b72d'));
$('#fvSwatchPaused').css('background-color', normalizeHexColor(form.status_color_paused?.value, deps.defaultFolderStatusColors?.paused || '#b8860b'));
$('#fvSwatchStopped').css('background-color', normalizeHexColor(form.status_color_stopped?.value, deps.defaultFolderStatusColors?.stopped || '#ff4d4d'));
+ $('#fvSwatchText').css('background-color', normalizeHexColor(form.status_color_text?.value, deps.defaultFolderStatusColors?.text || '#ffffff'));
$('#fvSwatchAccent').css('background-color', accentColor);
$('#fvAccentSwatchItem').toggle(accentEnabled);
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.schema.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.schema.js
index 319fb44e..f09b5f49 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.schema.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.schema.js
@@ -28,6 +28,7 @@
preview: Object.freeze({ title: 'Preview', description: 'How the folder row, preview layout, borders, and context surface render.', icon: 'fa-eye', advanced: false, supportsDefaults: true, supportsRevert: true }),
chevron: Object.freeze({ title: 'Chevron', description: 'Chevron style, normal color, hover color, and interaction styling.', icon: 'fa-chevron-down', advanced: false, supportsDefaults: true, supportsRevert: true }),
status: Object.freeze({ title: 'Status', description: 'Status palette, accent styling, and optional folder health or status thresholds.', icon: 'fa-heartbeat', advanced: false, supportsDefaults: true, supportsRevert: true }),
+ webuiProfiles: Object.freeze({ title: 'WebUI Profiles', description: 'Create named groups that open only selected Docker container WebUIs.', icon: 'fa-external-link', advanced: false, supportsDefaults: false, supportsRevert: false }),
rules: Object.freeze({ title: 'Rules', description: 'Managed auto-assignment rules, with conversion support for older folder regex data.', icon: 'fa-code', advanced: true, supportsDefaults: true, supportsRevert: true }),
actions: Object.freeze({ title: 'Actions', description: 'Quick actions and custom folder actions available from the folder menu.', icon: 'fa-bolt', advanced: true, supportsDefaults: false, supportsRevert: false }),
advanced: Object.freeze({ title: 'Advanced', description: 'Override behavior, expansion defaults, dashboard behavior, and niche controls.', icon: 'fa-sliders', advanced: true, supportsDefaults: true, supportsRevert: true })
@@ -72,6 +73,7 @@
'status_color_started',
'status_color_paused',
'status_color_stopped',
+ 'status_color_text',
'status_color_lock',
'health_warn_stopped_percent',
'health_critical_stopped_percent',
@@ -80,6 +82,7 @@
'health_all_stopped_mode',
'status_warn_stopped_percent'
]),
+ webuiProfiles: Object.freeze(['webui_profiles']),
rules: Object.freeze(['regex']),
advanced: Object.freeze(['update_column', 'override_default_actions', 'default_action', 'expand_tab', 'expand_dashboard', 'dashboard_overflow'])
});
@@ -123,9 +126,10 @@
status: Object.freeze({
folder_accent_enabled: false,
folder_accent_color: deps.defaultFolderAccentColor || '#ffca63',
- status_color_started: deps.defaultFolderStatusColors?.started || '#ffffff',
+ status_color_started: deps.defaultFolderStatusColors?.started || '#55b72d',
status_color_paused: deps.defaultFolderStatusColors?.paused || '#b8860b',
status_color_stopped: deps.defaultFolderStatusColors?.stopped || '#ff4d4d',
+ status_color_text: deps.defaultFolderStatusColors?.text || '#ffffff',
status_color_lock: false,
health_warn_stopped_percent: '',
health_critical_stopped_percent: '',
@@ -170,6 +174,7 @@
icon: 'Folder icon',
folder_webui: 'Folder WebUI',
folder_webui_url: 'Folder WebUI URL',
+ webui_profiles: 'WebUI profiles',
preview: 'Preview mode',
preview_hover: 'Hover-only preview',
preview_update: 'Update highlighting',
@@ -206,6 +211,7 @@
status_color_started: 'Started status color',
status_color_paused: 'Paused status color',
status_color_stopped: 'Stopped status color',
+ status_color_text: 'Status text color',
status_color_lock: 'Lock status colors',
health_warn_stopped_percent: 'Health warn threshold',
health_critical_stopped_percent: 'Health critical threshold',
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.shared.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.shared.js
index 5ce8d742..5ddba776 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.shared.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.shared.js
@@ -227,15 +227,28 @@
folder_accent_color: typeof deps.normalizeHexColor === 'function'
? deps.normalizeHexColor(settings.folder_accent_color, deps.defaultFolderAccentColor || '#ffca63')
: String(settings.folder_accent_color || deps.defaultFolderAccentColor || '#ffca63'),
- status_color_started: typeof deps.normalizeHexColor === 'function'
- ? deps.normalizeHexColor(settings.status_color_started, deps.defaultFolderStatusColors?.started || '#ffffff')
- : String(settings.status_color_started || deps.defaultFolderStatusColors?.started || '#ffffff'),
+ status_color_started: (() => {
+ const defaultStarted = deps.defaultFolderStatusColors?.started || '#55b72d';
+ const normalizedStarted = typeof deps.normalizeHexColor === 'function'
+ ? deps.normalizeHexColor(settings.status_color_started, defaultStarted)
+ : String(settings.status_color_started || defaultStarted).toLowerCase();
+ const startedExplicit = settings.status_color_started_explicit === true
+ || settings.statusColorStartedExplicit === true;
+ return !startedExplicit && normalizedStarted === '#ffffff'
+ ? defaultStarted
+ : normalizedStarted;
+ })(),
+ status_color_started_explicit: settings.status_color_started_explicit === true
+ || settings.statusColorStartedExplicit === true,
status_color_paused: typeof deps.normalizeHexColor === 'function'
? deps.normalizeHexColor(settings.status_color_paused, deps.defaultFolderStatusColors?.paused || '#b8860b')
: String(settings.status_color_paused || deps.defaultFolderStatusColors?.paused || '#b8860b'),
status_color_stopped: typeof deps.normalizeHexColor === 'function'
? deps.normalizeHexColor(settings.status_color_stopped, deps.defaultFolderStatusColors?.stopped || '#ff4d4d')
: String(settings.status_color_stopped || deps.defaultFolderStatusColors?.stopped || '#ff4d4d'),
+ status_color_text: typeof deps.normalizeHexColor === 'function'
+ ? deps.normalizeHexColor(settings.status_color_text, deps.defaultFolderStatusColors?.text || '#ffffff')
+ : String(settings.status_color_text || deps.defaultFolderStatusColors?.text || '#ffffff'),
status_color_lock: settings.status_color_lock === true || settings.statusColorLock === true,
health_warn_stopped_percent: parseOptionalThresholdInput(settings.health_warn_stopped_percent),
health_critical_stopped_percent: parseOptionalThresholdInput(settings.health_critical_stopped_percent),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.type-docker.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.type-docker.js
index 65aa08f7..a39a9bc8 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.type-docker.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.type-docker.js
@@ -124,6 +124,8 @@
State: state,
RawState: state,
UpdateAvailable: manager === 'dockerman' && updated === false,
+ WebUi: String(entry?.WebUi || entry?.WebUI || entry?.webui || '').trim(),
+ webuiCapability: entry?.webuiCapability === true ? true : (entry?.webuiCapability === false ? false : null),
Identity: {
kind: 'docker',
containerId: String(entry?.shortId || entry?.id || entry?.Id || '').replace(/^sha256:/i, '').slice(0, 64),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js
index 6f71202a..9bfc28b5 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.js
@@ -278,10 +278,11 @@ const folderParentPickerModule = window.FolderViewPlusFolderEditorParentPicker |
const folderIconApiModule = window.FolderViewPlusFolderIconApi || null;
const folderEditorRegexSelectionModule = window.FolderViewPlusFoundationModules?.folderEditorRegexSelection || null;
const folderEditorMemberListModule = window.FolderViewPlusFoundationModules?.folderEditorMemberList || null;
+const folderWebuiProfilesModule = window.FolderViewPlusFoundationModules?.folderWebuiProfiles || null;
const DEFAULT_FOLDER_STATUS_COLORS = folderContract?.DEFAULT_FOLDER_STATUS_COLORS || {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d', text: '#ffffff'
};
const DEFAULT_FOLDER_ACCENT_COLOR = folderContract?.DEFAULT_FOLDER_ACCENT_COLOR || '#ffca63';
const DEFAULT_BORDER_COLOR = folderContract?.DEFAULT_PREVIEW_BORDER_COLOR || '#afa89e';
@@ -402,6 +403,7 @@ if (!folderEditorRegexSelectionModule || typeof folderEditorRegexSelectionModule
if (!folderEditorMemberListModule || typeof folderEditorMemberListModule.createApi !== 'function' || typeof folderEditorMemberListModule.normalizeChildFolderOrder !== 'function') {
folderEditorBootstrapMissingModules.push('folder.editor.member-list.js');
}
+if (!folderWebuiProfilesModule || typeof folderWebuiProfilesModule.createEditorApi !== 'function') folderEditorBootstrapMissingModules.push('folder.webui-profiles.js');
if (!bulkAssignmentSharedModule || typeof bulkAssignmentSharedModule.createApi !== 'function') {
folderEditorBootstrapMissingModules.push('folderviewplus.bulk-assignment.shared.js');
}
@@ -441,7 +443,7 @@ let folderEditorPreviewRuntimeApi = null;
let folderEditorStateApi = null;
let folderEditorMembersApi = null;
let folderEditorRegexSelectionApi = null;
-let folderEditorMemberListApi = null;
+let folderEditorMemberListApi = null, folderWebuiProfilesApi = null;
let folderEditorIconsApi = null;
let folderEditorTypeApi = null;
let folderBulkAssignmentSharedApi = null;
@@ -452,7 +454,7 @@ let editorRecalcTimer = null;
let nameRegexSyncTimer = null;
let lastNameRegexSyncValue = '';
let editorMode = 'basic';
-let activeEditorSection = 'general';
+let activeEditorSection = folderEditorHashParams.get('section') || folderEditorQueryParams.get('section') || 'general';
let advancedSectionCollapsedState = {};
let memberBulkMoveInFlight = false;
let memberBulkMoveUndoState = null;
@@ -480,7 +482,7 @@ const SMART_DEFAULT_FIELD_NAMES = new Set([
'folder_accent_color',
'status_color_started',
'status_color_paused',
- 'status_color_stopped',
+ 'status_color_stopped', 'status_color_text',
'status_color_lock'
]);
@@ -1072,9 +1074,9 @@ const buildParentSmartDefaults = (parentFolder) => {
dropdown_hover_color: normalizeHexColor(settings.dropdown_hover_color, DEFAULT_DROPDOWN_HOVER_COLOR),
folder_accent_enabled: isFolderAccentEnabled(settings),
folder_accent_color: normalizeHexColor(settings.folder_accent_color, DEFAULT_FOLDER_ACCENT_COLOR),
- status_color_started: normalizeHexColor(settings.status_color_started, DEFAULT_FOLDER_STATUS_COLORS.started),
+ status_color_started: utils?.getFolderStatusColors?.(settings)?.started || normalizeHexColor(settings.status_color_started, DEFAULT_FOLDER_STATUS_COLORS.started),
status_color_paused: normalizeHexColor(settings.status_color_paused, DEFAULT_FOLDER_STATUS_COLORS.paused),
- status_color_stopped: normalizeHexColor(settings.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped),
+ status_color_stopped: normalizeHexColor(settings.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped), status_color_text: normalizeHexColor(settings.status_color_text, DEFAULT_FOLDER_STATUS_COLORS.text),
status_color_lock: settings.status_color_lock === true || settings.statusColorLock === true
};
};
@@ -1316,23 +1318,20 @@ const getFolderEditorIconsApi = () => {
return folderEditorIconsApi;
};
-const renderBuiltInIconPicker = () => {
- getFolderEditorIconsApi()?.renderBuiltInIconPicker();
-};
-
-const bindIconPickerEvents = async () => {
- await getFolderEditorIconsApi()?.bindIconPickerEvents();
-};
+const renderBuiltInIconPicker = () => { getFolderEditorIconsApi()?.renderBuiltInIconPicker(); };
+const bindIconPickerEvents = async () => { await getFolderEditorIconsApi()?.bindIconPickerEvents(); };
const getAllMembers = () => {
const map = new Map();
[...selectedRegex, ...selected, ...choose].forEach((member) => {
- if (!map.has(member.Name)) {
- map.set(member.Name, member);
- }
+ if (!map.has(member.Name)) map.set(member.Name, member);
});
return [...map.values()];
};
-
+const getFolderWebuiProfilesApi = () => {
+ if (folderWebuiProfilesApi || typeof folderWebuiProfilesModule?.createEditorApi !== 'function') return folderWebuiProfilesApi;
+ folderWebuiProfilesApi = folderWebuiProfilesModule.createEditorApi({ window, document, form: getForm(), type, getMembers: getAllMembers, translate: folderEditorT });
+ return folderWebuiProfilesApi;
+};
const computeFormSnapshot = () => {
const form = getForm();
const state = {
@@ -1341,12 +1340,10 @@ const computeFormSnapshot = () => {
childFolders: [],
actions: $('input[name*="custom_action"]').map((_, el) => $(el).val()).get()
};
-
$(form).find(':input[name]').each((_, element) => {
if (!element.name) {
return;
}
-
const value = element.type === 'checkbox' ? element.checked : $(element).val();
if (Object.prototype.hasOwnProperty.call(state.fields, element.name)) {
if (!Array.isArray(state.fields[element.name])) {
@@ -1357,7 +1354,6 @@ const computeFormSnapshot = () => {
state.fields[element.name] = value;
}
});
-
$('table.sortable > tbody > tr').each((_, row) => {
const input = $(row).find('input.container-switch');
state.members.push({
@@ -1374,7 +1370,6 @@ const computeFormSnapshot = () => {
return JSON.stringify(state);
};
-
const getFolderEditorStateApi = () => {
if (folderEditorStateApi || typeof folderEditorStateModule?.createApi !== 'function') {
return folderEditorStateApi;
@@ -1744,6 +1739,7 @@ const registerFolderEditorModuleTeardown = () => {
folderThemeSurfaceBinding?.disconnect();
folderEditorRegexSelectionApi?.dispose();
folderEditorMemberListApi?.dispose();
+ folderWebuiProfilesApi?.dispose();
}, { once: true });
};
@@ -1751,7 +1747,7 @@ const resetStatusColorDefaults = () => {
const form = $('div.canvas > form')[0];
form.status_color_started.value = DEFAULT_FOLDER_STATUS_COLORS.started;
form.status_color_paused.value = DEFAULT_FOLDER_STATUS_COLORS.paused;
- form.status_color_stopped.value = DEFAULT_FOLDER_STATUS_COLORS.stopped;
+ form.status_color_stopped.value = DEFAULT_FOLDER_STATUS_COLORS.stopped; form.status_color_text.value = DEFAULT_FOLDER_STATUS_COLORS.text;
if (typeof scheduleEditorRecalculation === 'function') {
scheduleEditorRecalculation(0);
}
@@ -2273,6 +2269,7 @@ const validateForm = () => {
validateParentFolderSelection(),
validateRegexField(),
validateFolderWebUiUrl(),
+ getFolderWebuiProfilesApi()?.validate() !== false,
validateContextGraphTime(),
validateHealthWarnThreshold(),
validateHealthCriticalThreshold(),
@@ -2953,7 +2950,7 @@ const initEditorChrome = () => {
Started
Paused
- Stopped
+ StoppedText
Accent
@@ -3058,7 +3055,7 @@ const initEditorChrome = () => {
updateMemberBulkMoveUi();
editorMode = loadEditorModePreference();
- activeEditorSection = normalizeActiveEditorSection('general', editorMode);
+ activeEditorSection = normalizeActiveEditorSection(activeEditorSection, editorMode);
advancedSectionCollapsedState = loadAdvancedCollapseState();
$('#fvRegexSimulatorInput').off('input').on('input', updateRegexSimulator);
$('#fvSuggestDefaults').off('click').on('click', suggestDefaultsFromMembers);
@@ -3157,6 +3154,7 @@ const hydrateCurrentEditFolder = (folderRecord, folderRecordId, foldersMap = {},
setFieldValue('icon', normalizedFolder.icon);
setFieldChecked('folder_webui', normalizedFolder.settings.folder_webui || false);
setFieldValue('folder_webui_url', normalizedFolder.settings.folder_webui_url || '');
+ getFolderWebuiProfilesApi()?.hydrate(normalizedFolder.settings.webui_profiles || normalizedFolder.settings.webuiProfiles || []);
setFieldValue('preview', String(normalizedFolder.settings.preview));
setFieldValue('preview_rows', String(normalizePreviewRowLimit(normalizedFolder.settings, normalizedFolder)));
setFieldValue('preview_overflow', normalizedFolder.settings.preview_overflow || normalizedFolder.settings.previewOverflow || 'default');
@@ -3195,7 +3193,7 @@ const hydrateCurrentEditFolder = (folderRecord, folderRecordId, foldersMap = {},
setFieldValue('folder_accent_color', normalizeHexColor(normalizedFolder.settings.folder_accent_color, DEFAULT_FOLDER_ACCENT_COLOR));
setFieldValue('status_color_started', normalizeHexColor(normalizedFolder.settings.status_color_started, DEFAULT_FOLDER_STATUS_COLORS.started));
setFieldValue('status_color_paused', normalizeHexColor(normalizedFolder.settings.status_color_paused, DEFAULT_FOLDER_STATUS_COLORS.paused));
- setFieldValue('status_color_stopped', normalizeHexColor(normalizedFolder.settings.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped));
+ setFieldValue('status_color_stopped', normalizeHexColor(normalizedFolder.settings.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped)); setFieldValue('status_color_text', normalizeHexColor(normalizedFolder.settings.status_color_text, DEFAULT_FOLDER_STATUS_COLORS.text));
setFieldChecked('status_color_lock', normalizedFolder.settings.status_color_lock === true || normalizedFolder.settings.statusColorLock === true);
setFieldValue('health_warn_stopped_percent', normalizedFolder.settings.health_warn_stopped_percent === undefined
|| normalizedFolder.settings.health_warn_stopped_percent === null
@@ -3506,6 +3504,7 @@ const startFolderEditorRuntime = async () => {
await bindIconPickerEvents();
updateList();
+ getFolderWebuiProfilesApi()?.refreshMembers();
applySectionTags();
initEditorChrome();
updateForm();
@@ -3797,6 +3796,7 @@ const buildFolderPayloadFromForm = (e) => {
settings: {
folder_webui: e.folder_webui.checked,
folder_webui_url: e.folder_webui_url.value.toString(),
+ webui_profiles: getFolderWebuiProfilesApi()?.serialize() || [],
preview: parseInt(e.preview.value.toString()),
preview_rows: normalizedPreviewRows,
preview_overflow: ['expand_row', 'scroll'].includes(String(e.preview_overflow?.value)) ? String(e.preview_overflow.value) : 'default',
@@ -3839,9 +3839,9 @@ const buildFolderPayloadFromForm = (e) => {
dropdown_hover_color: normalizeHexColor(e.dropdown_hover_color.value.toString(), DEFAULT_DROPDOWN_HOVER_COLOR),
folder_accent_enabled: e.folder_accent_enabled.checked,
folder_accent_color: normalizeHexColor(e.folder_accent_color.value.toString(), DEFAULT_FOLDER_ACCENT_COLOR),
- status_color_started: normalizeHexColor(e.status_color_started.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.started),
+ status_color_started: normalizeHexColor(e.status_color_started.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.started), status_color_started_explicit: true,
status_color_paused: normalizeHexColor(e.status_color_paused.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.paused),
- status_color_stopped: normalizeHexColor(e.status_color_stopped.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.stopped),
+ status_color_stopped: normalizeHexColor(e.status_color_stopped.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.stopped), status_color_text: normalizeHexColor(e.status_color_text.value.toString(), DEFAULT_FOLDER_STATUS_COLORS.text),
status_color_lock: e.status_color_lock?.checked === true,
health_warn_stopped_percent: healthWarnThreshold,
health_critical_stopped_percent: healthCriticalThreshold,
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.member-identity.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.member-identity.js
index 5db8a342..5c33635e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.member-identity.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.member-identity.js
@@ -1,12 +1,12 @@
// @ts-check
(function(root, factory) {
if (typeof module === 'object' && module.exports) {
- module.exports = factory();
+ module.exports = factory(require('./folder.webui-profiles.js'));
return;
}
- root.FolderViewPlusMemberIdentity = factory();
+ root.FolderViewPlusMemberIdentity = factory(root.FolderViewPlusFoundationModules?.folderWebuiProfiles);
root.FolderViewPlusMemberIdentityModuleLoaded = true;
-}(typeof globalThis !== 'undefined' ? globalThis : this, function() {
+}(typeof globalThis !== 'undefined' ? globalThis : this, function(webuiProfiles) {
'use strict';
const asObject = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
@@ -197,6 +197,7 @@
folder.memberIdentities = normalizeIdentityMap(folder.memberIdentities || folder.member_identities, safeType);
folder.hiddenPreviewMembers = uniqueStrings(folder.hiddenPreviewMembers || folder.hidden_preview);
folder.actions = Array.isArray(folder.actions) ? folder.actions.map((action) => ({ ...asObject(action) })) : [];
+ folder.settings = { ...asObject(folder.settings) };
folder.containers.forEach((name) => {
if (liveNames.has(name)) {
claimedNames.add(name);
@@ -234,6 +235,9 @@
folder.containers = renameListEntry(folder.containers, oldName, newName);
folder.hiddenPreviewMembers = renameListEntry(folder.hiddenPreviewMembers, oldName, newName);
folder.actions = renameActionTargets(folder.actions, oldName, newName);
+ if (typeof webuiProfiles?.renameProfileMember === 'function') {
+ folder.settings.webui_profiles = webuiProfiles.renameProfileMember(folder.settings.webui_profiles, oldName, newName);
+ }
delete folder.memberIdentities[oldName];
folder.memberIdentities[newName] = resolution.candidate.identity;
renames[oldName] = newName;
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js
index 4b14c06a..8094ae35 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.settings-transfer.js
@@ -38,6 +38,8 @@
const normalizedSettings = source.settings && typeof source.settings === 'object'
? deepClone(source.settings) || {}
: {};
+ delete normalizedSettings.webui_profiles;
+ delete normalizedSettings.webuiProfiles;
const actions = Array.isArray(source.actions) ? source.actions : [];
const normalizedActions = [];
let droppedMemberBoundActionCount = 0;
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js
new file mode 100644
index 00000000..6db80b2c
--- /dev/null
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js
@@ -0,0 +1,446 @@
+// @ts-check
+(function(root, factory) {
+ if (typeof module === 'object' && module.exports) {
+ module.exports = factory();
+ return;
+ }
+ const modules = root.FolderViewPlusFoundationModules = root.FolderViewPlusFoundationModules || {};
+ modules.folderWebuiProfiles = factory();
+}(typeof globalThis !== 'undefined' ? globalThis : this, function() {
+ 'use strict';
+
+ const MAX_PROFILES = 100;
+ const MAX_PROFILE_MEMBERS = 250;
+ const MAX_PROFILE_NAME_LENGTH = 80;
+ const MAX_MEMBER_NAME_LENGTH = 160;
+ const PROFILE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,64}$/;
+ const isObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
+ const cleanText = (value, maxLength) => String(value ?? '')
+ .replace(/[\u0000-\u001f\u007f]/g, '')
+ .trim()
+ .slice(0, maxLength);
+ const uniqueStrings = (value, maxItems = MAX_PROFILE_MEMBERS) => {
+ const seen = new Set();
+ const result = [];
+ for (const entry of Array.isArray(value) ? value : []) {
+ const text = cleanText(entry, MAX_MEMBER_NAME_LENGTH);
+ if (!text || seen.has(text)) continue;
+ seen.add(text);
+ result.push(text);
+ if (result.length >= maxItems) break;
+ }
+ return result;
+ };
+ const createProfileId = (win = null) => {
+ try {
+ const generated = win?.crypto?.randomUUID?.();
+ if (generated && PROFILE_ID_PATTERN.test(generated)) return generated;
+ } catch (_error) {
+ // Fall through to a locally unique identifier.
+ }
+ return `profile-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
+ };
+ const normalizeProfiles = (value, options = {}) => {
+ const source = Array.isArray(value) ? value : [];
+ const maxProfiles = Math.max(1, Math.min(MAX_PROFILES, Number(options.maxProfiles) || MAX_PROFILES));
+ const usedIds = new Set();
+ const result = [];
+ for (let index = 0; index < source.length && result.length < maxProfiles; index += 1) {
+ const entry = isObject(source[index]) ? source[index] : null;
+ if (!entry) continue;
+ let id = cleanText(entry.id, 64);
+ if (!PROFILE_ID_PATTERN.test(id)) id = `profile-${index + 1}`;
+ const baseId = id;
+ let suffix = 2;
+ while (usedIds.has(id)) {
+ id = `${baseId.slice(0, 58)}-${suffix}`;
+ suffix += 1;
+ }
+ usedIds.add(id);
+ result.push({
+ id,
+ name: cleanText(entry.name, MAX_PROFILE_NAME_LENGTH) || `Profile ${result.length + 1}`,
+ containers: uniqueStrings(entry.containers || entry.members)
+ });
+ }
+ return result;
+ };
+ const stripProfilesFromSettings = (settings) => {
+ const next = isObject(settings) ? JSON.parse(JSON.stringify(settings)) : {};
+ delete next.webui_profiles;
+ delete next.webuiProfiles;
+ return next;
+ };
+ const renameProfileMember = (profiles, oldName, newName) => normalizeProfiles(profiles).map((profile) => ({
+ ...profile,
+ containers: uniqueStrings(profile.containers.map((name) => name === oldName ? newName : name))
+ }));
+ const getRuntimeEntryName = (fallbackName, entry) => cleanText(
+ entry?.name || entry?.Name || entry?.info?.Name || fallbackName,
+ MAX_MEMBER_NAME_LENGTH
+ );
+ const collectProfileTargets = (profile, runtimeEntries, options = {}) => {
+ const normalized = normalizeProfiles([profile])[0] || null;
+ if (!normalized) return { profile: null, urls: [], selectedCount: 0, eligibleCount: 0, unavailableCount: 0 };
+ const selected = new Set(normalized.containers);
+ const matched = new Set();
+ const urls = [];
+ const getSafeWebuiUrl = typeof options.getSafeWebuiUrl === 'function'
+ ? options.getSafeWebuiUrl
+ : ((url) => cleanText(url, 2048));
+ const runningOnly = options.runningOnly !== false;
+ Object.entries(isObject(runtimeEntries) ? runtimeEntries : {}).forEach(([fallbackName, entry]) => {
+ const name = getRuntimeEntryName(fallbackName, entry);
+ if (!name || !selected.has(name)) return;
+ matched.add(name);
+ const running = entry?.state === true && entry?.pause !== true;
+ const url = getSafeWebuiUrl(entry?.webui || entry?.WebUi || entry?.WebUI || '');
+ if (url && (!runningOnly || running)) urls.push(url);
+ });
+ const dedupedUrls = Array.from(new Set(urls));
+ return {
+ profile: normalized,
+ urls: dedupedUrls,
+ selectedCount: normalized.containers.length,
+ eligibleCount: dedupedUrls.length,
+ unavailableCount: Math.max(0, normalized.containers.length - matched.size)
+ };
+ };
+ const summarizeProfiles = (profiles, availableMemberNames = []) => {
+ const available = new Set(uniqueStrings(availableMemberNames, 5000));
+ const normalized = normalizeProfiles(profiles);
+ let selectedReferenceCount = 0;
+ let unavailableReferenceCount = 0;
+ normalized.forEach((profile) => {
+ selectedReferenceCount += profile.containers.length;
+ unavailableReferenceCount += profile.containers.filter((name) => !available.has(name)).length;
+ });
+ return {
+ profileCount: normalized.length,
+ selectedReferenceCount,
+ unavailableReferenceCount,
+ duplicateProfileIdCount: 0,
+ invalidProfileCount: 0
+ };
+ };
+ const resolveProfileLaunch = (settings, profileId, runtimeEntries, options = {}) => {
+ const profile = normalizeProfiles(settings?.webui_profiles || settings?.webuiProfiles)
+ .find((entry) => entry.id === cleanText(profileId, 64)) || null;
+ return profile ? collectProfileTargets(profile, runtimeEntries, options) : null;
+ };
+ const appendRuntimeMenuItems = (target, options = {}) => {
+ const profiles = normalizeProfiles(options.profiles);
+ if (!Array.isArray(target) || profiles.length === 0) return false;
+ const translate = typeof options.translate === 'function' ? options.translate : ((_key, fallback) => fallback);
+ const onOpen = typeof options.onOpen === 'function' ? options.onOpen : (() => {});
+ const onManage = typeof options.onManage === 'function' ? options.onManage : (() => {});
+ target.push({
+ text: translate('docker.webui-profiles.open', 'Open WebUI profile'),
+ icon: 'fa-list-alt',
+ subMenu: profiles.map((profile) => {
+ const result = collectProfileTargets(profile, options.runtimeEntries, {
+ runningOnly: true,
+ getSafeWebuiUrl: options.getSafeWebuiUrl
+ });
+ return {
+ text: translate('docker.webui-profiles.ready', '$1 ($2 of $3 ready)', profile.name, result.eligibleCount, result.selectedCount),
+ icon: 'fa-external-link',
+ disabled: result.eligibleCount === 0,
+ action: (event) => { event?.preventDefault?.(); onOpen(profile.id); }
+ };
+ })
+ }, {
+ text: translate('docker.webui-profiles.manage', 'Manage WebUI profiles'),
+ icon: 'fa-sliders',
+ action: (event) => { event?.preventDefault?.(); onManage(); }
+ });
+ return true;
+ };
+
+ const createEditorApi = (deps = {}) => {
+ const win = deps.window || (typeof window !== 'undefined' ? window : null);
+ const doc = deps.document || win?.document || null;
+ const form = deps.form || doc?.getElementById?.('fvFolderEditorForm') || null;
+ const getMembers = typeof deps.getMembers === 'function' ? deps.getMembers : (() => []);
+ const translate = typeof deps.translate === 'function' ? deps.translate : ((_key, fallback) => fallback);
+ const onChange = typeof deps.onChange === 'function' ? deps.onChange : (() => {});
+ const type = cleanText(deps.type, 16).toLowerCase() === 'docker' ? 'docker' : 'vm';
+ const host = form?.querySelector?.('#fvWebuiProfilesWorkspace') || null;
+ const hiddenInput = form?.querySelector?.('input[name="webui_profiles"]') || null;
+ let profiles = [];
+ let disposed = false;
+ let memberRefreshTimer = 0;
+
+ const readIncludedMembers = () => {
+ const allMembers = new Map((Array.isArray(getMembers()) ? getMembers() : [])
+ .map((member) => [cleanText(member?.Name || member?.name, MAX_MEMBER_NAME_LENGTH), member])
+ .filter(([name]) => Boolean(name)));
+ const names = Array.from(form?.querySelectorAll?.('input[name*="containers"]:checked') || [])
+ .map((input) => cleanText(input.value, MAX_MEMBER_NAME_LENGTH))
+ .filter(Boolean);
+ return names.map((name) => {
+ const member = allMembers.get(name) || {};
+ const rawState = member?.State || member?.RawState || {};
+ const state = rawState?.Paused === true
+ ? 'paused'
+ : (rawState?.Running === true ? 'running' : 'stopped');
+ return {
+ name,
+ state,
+ hasWebui: Boolean(member?.WebUi || member?.WebUI || member?.webui || member?.webuiCapability === true)
+ };
+ });
+ };
+ const writeHiddenInput = (notify = true) => {
+ if (!hiddenInput) return;
+ hiddenInput.value = JSON.stringify(profiles.map((profile) => ({
+ id: profile.id,
+ name: cleanText(profile.name, MAX_PROFILE_NAME_LENGTH),
+ containers: uniqueStrings(profile.containers)
+ })));
+ if (notify) {
+ hiddenInput.dispatchEvent(new win.Event('change', { bubbles: true }));
+ onChange(profiles);
+ }
+ };
+ const button = (label, icon, action, className = '') => {
+ const node = doc.createElement('button');
+ node.type = 'button';
+ node.className = `fv-webui-profile-button ${className}`.trim();
+ node.setAttribute('aria-label', label);
+ const glyph = doc.createElement('i');
+ glyph.className = `fa ${icon}`;
+ glyph.setAttribute('aria-hidden', 'true');
+ const text = doc.createElement('span');
+ text.textContent = label;
+ node.append(glyph, text);
+ node.addEventListener('click', action);
+ return node;
+ };
+ const setStatus = (message = '', tone = '') => {
+ const node = host?.querySelector?.('.fv-webui-profiles-status');
+ if (!node) return;
+ node.textContent = message;
+ node.className = `fv-webui-profiles-status${tone ? ` is-${tone}` : ''}`;
+ };
+ const validate = () => {
+ if (type !== 'docker') return true;
+ const names = new Set();
+ let message = '';
+ profiles.forEach((profile) => {
+ const name = cleanText(profile.name, MAX_PROFILE_NAME_LENGTH);
+ const key = name.toLocaleLowerCase();
+ if (!message && !name) message = translate('editor.webui-profiles.error-name', 'Every WebUI profile needs a name.');
+ if (!message && names.has(key)) message = translate('editor.webui-profiles.error-duplicate', 'WebUI profile names must be unique within this folder.');
+ names.add(key);
+ if (!message && uniqueStrings(profile.containers).length === 0) {
+ message = translate('editor.webui-profiles.error-members', 'Every WebUI profile must select at least one folder member.');
+ }
+ });
+ Array.from(host?.querySelectorAll?.('.fv-webui-profile-name') || []).forEach((input) => {
+ const name = cleanText(input.value, MAX_PROFILE_NAME_LENGTH).toLocaleLowerCase();
+ const count = profiles.filter((profile) => cleanText(profile.name, MAX_PROFILE_NAME_LENGTH).toLocaleLowerCase() === name).length;
+ input.classList.toggle('fv-input-error', !name || count > 1);
+ input.setAttribute('aria-invalid', !name || count > 1 ? 'true' : 'false');
+ });
+ setStatus(message, message ? 'error' : 'ready');
+ return !message;
+ };
+ const renderMemberRows = (profile, list, query = '') => {
+ const members = readIncludedMembers();
+ const availableNames = new Set(members.map((member) => member.name));
+ profile.containers.forEach((name) => {
+ if (!availableNames.has(name)) members.push({ name, state: 'unavailable', hasWebui: false });
+ });
+ const normalizedQuery = cleanText(query, 160).toLocaleLowerCase();
+ list.replaceChildren();
+ members.forEach((member) => {
+ if (normalizedQuery && !member.name.toLocaleLowerCase().includes(normalizedQuery)) return;
+ const row = doc.createElement('label');
+ row.className = `fv-webui-profile-member is-${member.state}`;
+ const checkbox = doc.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.checked = profile.containers.includes(member.name);
+ checkbox.value = member.name;
+ checkbox.addEventListener('change', () => {
+ profile.containers = checkbox.checked
+ ? uniqueStrings([...profile.containers, member.name])
+ : profile.containers.filter((name) => name !== member.name);
+ writeHiddenInput();
+ validate();
+ });
+ const name = doc.createElement('strong');
+ name.textContent = member.name;
+ const meta = doc.createElement('span');
+ const webuiLabel = member.hasWebui
+ ? translate('editor.webui-profiles.webui-ready', 'WebUI configured')
+ : translate('editor.webui-profiles.webui-runtime', 'WebUI checked at launch');
+ meta.textContent = member.state === 'unavailable'
+ ? translate('editor.webui-profiles.unavailable', 'Unavailable in this folder')
+ : `${member.state} · ${webuiLabel}`;
+ row.append(checkbox, name, meta);
+ list.appendChild(row);
+ });
+ if (!list.children.length) {
+ const empty = doc.createElement('p');
+ empty.className = 'fv-webui-profile-empty-members';
+ empty.textContent = translate('editor.webui-profiles.no-members', 'No matching folder members are available.');
+ list.appendChild(empty);
+ }
+ };
+ const renderProfile = (profile, index) => {
+ const card = doc.createElement('article');
+ card.className = 'fv-webui-profile-card';
+ card.dataset.profileId = profile.id;
+ const header = doc.createElement('div');
+ header.className = 'fv-webui-profile-head';
+ const nameInput = doc.createElement('input');
+ nameInput.type = 'text';
+ nameInput.className = 'fv-webui-profile-name';
+ nameInput.maxLength = MAX_PROFILE_NAME_LENGTH;
+ nameInput.value = profile.name;
+ nameInput.setAttribute('aria-label', translate('editor.webui-profiles.name', 'Profile name'));
+ nameInput.addEventListener('input', () => {
+ profile.name = nameInput.value;
+ writeHiddenInput();
+ validate();
+ });
+ const actions = doc.createElement('div');
+ actions.className = 'fv-webui-profile-actions';
+ actions.append(
+ button(translate('editor.webui-profiles.duplicate', 'Duplicate'), 'fa-copy', () => {
+ if (profiles.length >= MAX_PROFILES) return;
+ profiles.splice(index + 1, 0, {
+ id: createProfileId(win),
+ name: `${cleanText(profile.name, 68) || 'Profile'} Copy`,
+ containers: [...profile.containers]
+ });
+ writeHiddenInput();
+ render();
+ }),
+ button(translate('editor.webui-profiles.move-up', 'Move up'), 'fa-chevron-up', () => {
+ if (index <= 0) return;
+ [profiles[index - 1], profiles[index]] = [profiles[index], profiles[index - 1]];
+ writeHiddenInput();
+ render();
+ }, 'is-icon'),
+ button(translate('editor.webui-profiles.move-down', 'Move down'), 'fa-chevron-down', () => {
+ if (index >= profiles.length - 1) return;
+ [profiles[index + 1], profiles[index]] = [profiles[index], profiles[index + 1]];
+ writeHiddenInput();
+ render();
+ }, 'is-icon'),
+ button(translate('editor.webui-profiles.delete', 'Delete'), 'fa-trash', () => {
+ profiles.splice(index, 1);
+ writeHiddenInput();
+ render();
+ }, 'is-danger')
+ );
+ header.append(nameInput, actions);
+ const tools = doc.createElement('div');
+ tools.className = 'fv-webui-profile-tools';
+ const search = doc.createElement('input');
+ search.type = 'search';
+ search.placeholder = translate('editor.webui-profiles.search', 'Search folder members');
+ search.setAttribute('aria-label', search.placeholder);
+ const list = doc.createElement('div');
+ list.className = 'fv-webui-profile-members';
+ search.addEventListener('input', () => renderMemberRows(profile, list, search.value));
+ tools.append(
+ search,
+ button(translate('editor.webui-profiles.select-webui', 'Select all with WebUI'), 'fa-check-square-o', () => {
+ profile.containers = uniqueStrings(readIncludedMembers().filter((member) => member.hasWebui).map((member) => member.name));
+ writeHiddenInput();
+ render();
+ }),
+ button(translate('editor.webui-profiles.clear', 'Clear selection'), 'fa-square-o', () => {
+ profile.containers = [];
+ writeHiddenInput();
+ render();
+ })
+ );
+ renderMemberRows(profile, list);
+ card.append(header, tools, list);
+ return card;
+ };
+ const render = () => {
+ if (!host || disposed) return;
+ host.replaceChildren();
+ if (type !== 'docker') {
+ host.closest?.('.basic')?.setAttribute?.('hidden', 'hidden');
+ return;
+ }
+ const toolbar = doc.createElement('div');
+ toolbar.className = 'fv-webui-profiles-toolbar';
+ const summary = doc.createElement('p');
+ summary.textContent = profiles.length
+ ? translate('editor.webui-profiles.count', '$1 saved profile(s)', profiles.length).replace('$1', String(profiles.length))
+ : translate('editor.webui-profiles.empty', 'No custom WebUI profiles yet.');
+ const add = button(translate('editor.webui-profiles.add', 'Add profile'), 'fa-plus', () => {
+ if (profiles.length >= MAX_PROFILES) {
+ setStatus(translate('editor.webui-profiles.limit', 'This folder has reached the WebUI profile safety limit.'), 'error');
+ return;
+ }
+ profiles.push({ id: createProfileId(win), name: `Profile ${profiles.length + 1}`, containers: [] });
+ writeHiddenInput();
+ render();
+ }, 'is-primary');
+ toolbar.append(summary, add);
+ host.appendChild(toolbar);
+ const cards = doc.createElement('div');
+ cards.className = 'fv-webui-profile-list';
+ profiles.forEach((profile, index) => cards.appendChild(renderProfile(profile, index)));
+ host.appendChild(cards);
+ const status = doc.createElement('p');
+ status.className = 'fv-webui-profiles-status';
+ status.setAttribute('aria-live', 'polite');
+ host.appendChild(status);
+ validate();
+ };
+ const hydrate = (value) => {
+ profiles = normalizeProfiles(value);
+ writeHiddenInput(false);
+ render();
+ };
+ const scheduleMemberRefresh = () => {
+ if (memberRefreshTimer) win.clearTimeout(memberRefreshTimer);
+ memberRefreshTimer = win.setTimeout(() => {
+ memberRefreshTimer = 0;
+ render();
+ }, 0);
+ };
+ const memberChangeHandler = (event) => {
+ if (event?.target?.matches?.('input[name*="containers"]')) scheduleMemberRefresh();
+ };
+ form?.addEventListener?.('change', memberChangeHandler);
+ hydrate([]);
+ return Object.freeze({
+ hydrate,
+ render,
+ refreshMembers: scheduleMemberRefresh,
+ serialize: () => normalizeProfiles(profiles),
+ validate,
+ snapshot: () => ({ profiles: normalizeProfiles(profiles), ...summarizeProfiles(profiles, readIncludedMembers().map((member) => member.name)) }),
+ dispose: () => {
+ disposed = true;
+ if (memberRefreshTimer) win.clearTimeout(memberRefreshTimer);
+ form?.removeEventListener?.('change', memberChangeHandler);
+ host?.replaceChildren?.();
+ }
+ });
+ };
+
+ return Object.freeze({
+ MAX_PROFILES,
+ MAX_PROFILE_MEMBERS,
+ MAX_PROFILE_NAME_LENGTH,
+ normalizeProfiles,
+ stripProfilesFromSettings,
+ renameProfileMember,
+ collectProfileTargets,
+ summarizeProfiles,
+ resolveProfileLaunch,
+ appendRuntimeMenuItems,
+ createEditorApi
+ });
+}));
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.folder-contract.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.folder-contract.js
index 827f214c..a87d8d91 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.folder-contract.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.folder-contract.js
@@ -3,10 +3,12 @@
'use strict';
const DEFAULT_FOLDER_STATUS_COLORS = Object.freeze({
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d',
+ text: '#ffffff'
});
+ const LEGACY_DEFAULT_FOLDER_STARTED_COLOR = '#ffffff';
const DEFAULT_FOLDER_ACCENT_COLOR = '#ffca63';
const DEFAULT_PREVIEW_BORDER_COLOR = '#afa89e';
const DEFAULT_PREVIEW_BORDER_WIDTH = 1;
@@ -141,6 +143,7 @@
window.FolderViewPlusFolderContract = {
DEFAULT_FOLDER_STATUS_COLORS,
+ LEGACY_DEFAULT_FOLDER_STARTED_COLOR,
DEFAULT_FOLDER_ACCENT_COLOR,
DEFAULT_PREVIEW_BORDER_COLOR,
DEFAULT_PREVIEW_BORDER_WIDTH,
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.starter-templates.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.starter-templates.js
index cc88dd85..5dfc619e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.starter-templates.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.starter-templates.js
@@ -566,9 +566,9 @@ const buildStarterFolderPayload = (name, iconPath = DEFAULT_STARTER_FOLDER_ICON)
preview_vertical_bars_color: '#afa89e',
folder_accent_enabled: false,
folder_accent_color: '#ffca63',
- status_color_started: '#ffffff',
+ status_color_started: '#55b72d',
status_color_paused: '#b8860b',
- status_color_stopped: '#ff4d4d',
+ status_color_stopped: '#ff4d4d', status_color_text: '#ffffff',
health_warn_stopped_percent: '',
health_critical_stopped_percent: '',
health_profile: '',
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js
index 50d6ba6d..263f1cb6 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.ui.js
@@ -41,6 +41,8 @@
'check-circle': '
',
'clock': '
',
'document': '
',
+ 'eye': '
',
+ 'eye-off': '
',
'folder': '
',
'globe': '
',
'heart': '
',
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-normalization.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-normalization.js
index 7a6c1c1e..35c22d11 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-normalization.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-normalization.js
@@ -28,9 +28,10 @@
const PERFORMANCE_PROFILE_OPTIONS = ['standard', 'adaptive', 'maximum'];
const RUNTIME_PAGE_VIEW_MODE_OPTIONS = ['folderview', 'host', 'command'];
const DEFAULT_FOLDER_STATUS_COLORS = {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d',
+ text: '#ffffff'
};
const DEFAULT_HEALTH_PREFS = {
cardsEnabled: true,
@@ -105,11 +106,10 @@
} = utilityFoundation;
const getFolderStatusColors = (settings) => {
- const source = isPlainObject(settings) ? settings : {};
- return {
- started: normalizeHexColor(source.status_color_started, DEFAULT_FOLDER_STATUS_COLORS.started),
+ const source = isPlainObject(settings) ? settings : {}; const normalizedStarted = normalizeHexColor(source.status_color_started, DEFAULT_FOLDER_STATUS_COLORS.started); const startedExplicit = source.status_color_started_explicit === true || source.statusColorStartedExplicit === true;
+ return { started: !startedExplicit && normalizedStarted === '#ffffff' ? DEFAULT_FOLDER_STATUS_COLORS.started : normalizedStarted,
paused: normalizeHexColor(source.status_color_paused, DEFAULT_FOLDER_STATUS_COLORS.paused),
- stopped: normalizeHexColor(source.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped)
+ stopped: normalizeHexColor(source.status_color_stopped, DEFAULT_FOLDER_STATUS_COLORS.stopped), text: normalizeHexColor(source.status_color_text, DEFAULT_FOLDER_STATUS_COLORS.text)
};
};
@@ -243,7 +243,7 @@
return Array.from(
new Set(
value
- .map((item) => String(item || '').trim())
+ .filter((item) => typeof item === 'string').map((item) => item.trim())
.filter((item) => item !== '')
)
);
@@ -279,7 +279,7 @@
return null;
}
- const name = String(value.name || '').trim();
+ const name = typeof value.name === 'string' ? value.name.trim() : '';
if (name === '') {
return null;
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-prefs.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-prefs.js
index 42fddae9..b636cc65 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-prefs.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-prefs.js
@@ -85,8 +85,8 @@
const folderDefaultsSettings = isPlainObject(folderDefaultsProfileRaw.settings)
? JSON.parse(JSON.stringify(folderDefaultsProfileRaw.settings))
: {};
- const folderDefaultsActionsRaw = Array.isArray(folderDefaultsProfileRaw.actions) ? folderDefaultsProfileRaw.actions : [];
- const folderDefaultsActions = [];
+ delete folderDefaultsSettings.webui_profiles; delete folderDefaultsSettings.webuiProfiles;
+ const folderDefaultsActionsRaw = Array.isArray(folderDefaultsProfileRaw.actions) ? folderDefaultsProfileRaw.actions : [], folderDefaultsActions = [];
folderDefaultsActionsRaw.forEach((entry) => {
if (!isPlainObject(entry)) {
return;
@@ -318,6 +318,7 @@
)
};
const pinnedFolderIds = normalizeStringIdList(incoming.pinnedFolderIds);
+ const hiddenFolderIds = normalizeStringIdList(incoming.hiddenFolderIds);
const expandedFolderState = normalizeExpandedFolderStateMap(incoming.expandedFolderState);
const hideEmptyFolders = incoming.hideEmptyFolders === true;
const appColumnWidth = normalizeAppColumnWidth(incoming.appColumnWidth);
@@ -346,6 +347,7 @@
sortMode,
manualOrder,
pinnedFolderIds,
+ hiddenFolderIds,
expandedFolderState,
hideEmptyFolders,
appColumnWidth,
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.page-diagnostics.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.page-diagnostics.js
index f226dc59..0b299692 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.page-diagnostics.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.page-diagnostics.js
@@ -78,8 +78,8 @@
highContrast: snapshot?.appearance?.highContrast === true
},
state: {
- visibleRows: boundedCount(snapshot?.state?.visibleRows),
- folderRows: boundedCount(snapshot?.state?.folderRows),
+ visibleRows: boundedCount(snapshot?.state?.visibleRows), folderRows: boundedCount(snapshot?.state?.folderRows),
+ hiddenFolderSelections: boundedCount(snapshot?.state?.hiddenFolderSelections), effectivelyHiddenFolders: boundedCount(snapshot?.state?.effectivelyHiddenFolders), revealedHiddenFolderRows: boundedCount(snapshot?.state?.revealedHiddenFolderRows), revealHiddenFolders: snapshot?.state?.revealHiddenFolders === true,
expandedFolders: boundedCount(snapshot?.state?.expandedFolders),
visibleMembers: boundedCount(snapshot?.state?.visibleMembers),
loadingIndicators: boundedCount(snapshot?.state?.loadingIndicators),
@@ -152,8 +152,8 @@
highContrast: matchesMedia(win, '(prefers-contrast: more)')
},
state: {
- visibleRows: countVisible(host, 'tr', win),
- folderRows: countVisible(host, '.folder, .folder-showcase-outer', win),
+ visibleRows: countVisible(host, 'tr', win), folderRows: countVisible(host, '.folder, .folder-showcase-outer', win),
+ hiddenFolderSelections: boundedCount(doc?.body?.getAttribute?.('data-fvplus-docker-hidden-explicit')), effectivelyHiddenFolders: boundedCount(doc?.body?.getAttribute?.('data-fvplus-docker-hidden-effective')), revealedHiddenFolderRows: countVisible(host, '.folder.fv-folder-hidden-revealed', win), revealHiddenFolders: doc?.body?.getAttribute?.('data-fvplus-docker-hidden-reveal') === 'true',
expandedFolders: countVisible(host, '.folder[expanded="true"], .folder-showcase-outer[expanded="true"]', win),
visibleMembers: countVisible(host, '.folder-preview-wrapper, .folder-element-docker, .folder-element-vm', win),
loadingIndicators: countVisible(host, '.spinner, .fv-runtime-loading-row, [aria-busy="true"]', win),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-primitives.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-primitives.js
index 58bdfbc1..a415cbdc 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-primitives.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-primitives.js
@@ -114,6 +114,7 @@
const clearClassName = String(options.clearClassName || 'fvplus-docker-quick-clear');
const labelSet = options.labelSet instanceof Set ? options.labelSet : new Set();
const iconClassCandidates = Array.isArray(options.iconClassCandidates) ? options.iconClassCandidates : [];
+ const minimumItems = Math.max(1, Number(options.minimumItems) || 3), maximumItems = Math.max(minimumItems, Number(options.maximumItems) || minimumItems);
const selectors = Array.isArray(options.selectors) && options.selectors.length
? options.selectors
: [
@@ -167,10 +168,8 @@
if (!$menu || !$menu.length) {
return false;
}
- const $quickItems = $menu.children('li').filter((_, item) => isQuickItem($(item))).slice(0, 3);
- if ($quickItems.length < 3) {
- return false;
- }
+ const $quickItems = $menu.children('li').filter((_, item) => isQuickItem($(item))).slice(0, maximumItems);
+ if ($quickItems.length < minimumItems) return false;
$menu.addClass(menuClassName);
$quickItems.each((_, item) => {
const $item = $(item);
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/vm.js b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/vm.js
index 5b274cb9..15635566 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/vm.js
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/vm.js
@@ -22,9 +22,9 @@ const applyVmThemeResolverTokens = (reason = 'vm-runtime:initial', options = {})
);
const DEFAULT_FOLDER_ICON_PATH = '/plugins/folderview.plus/images/folder-icon.png';
const localDefaultFolderStatusColors = runtimeShared.DEFAULT_FOLDER_STATUS_COLORS || {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d', text: '#ffffff'
};
const normalizeStatusHexColor = typeof runtimeShared.normalizeStatusHexColor === 'function'
? runtimeShared.normalizeStatusHexColor
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-integrity.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-integrity.php
index de8c1ce6..0ac0f66b 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-integrity.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-integrity.php
@@ -88,7 +88,7 @@ function diagnosticsFolderStatusColors(array $folder): array {
return [
'started' => diagnosticsNormalizeStatusColor($settings['status_color_started'] ?? null, FVPLUS_DEFAULT_FOLDER_STATUS_COLORS['started']),
'paused' => diagnosticsNormalizeStatusColor($settings['status_color_paused'] ?? null, FVPLUS_DEFAULT_FOLDER_STATUS_COLORS['paused']),
- 'stopped' => diagnosticsNormalizeStatusColor($settings['status_color_stopped'] ?? null, FVPLUS_DEFAULT_FOLDER_STATUS_COLORS['stopped'])
+ 'stopped' => diagnosticsNormalizeStatusColor($settings['status_color_stopped'] ?? null, FVPLUS_DEFAULT_FOLDER_STATUS_COLORS['stopped']), 'text' => diagnosticsNormalizeStatusColor($settings['status_color_text'] ?? null, FVPLUS_DEFAULT_FOLDER_STATUS_COLORS['text'])
];
}
@@ -664,13 +664,8 @@ function diagnosticsBuildIntegrityChecks(string $type, array $folders, array $pr
$missingManualOrderIds[] = $manualId;
}
}
- $missingPinnedFolderIds = [];
- foreach (($prefs['pinnedFolderIds'] ?? []) as $pinnedId) {
- $pinnedId = (string)$pinnedId;
- if ($pinnedId !== '' && !array_key_exists($pinnedId, $folders)) {
- $missingPinnedFolderIds[] = $pinnedId;
- }
- }
+ $missingPreferenceFolderIds = static fn($ids): array => array_values(array_filter(normalizeStringIdList($ids), static fn($id): bool => !array_key_exists((string)$id, $folders)));
+ $missingPinnedFolderIds = $missingPreferenceFolderIds($prefs['pinnedFolderIds'] ?? []); $missingHiddenFolderIds = $missingPreferenceFolderIds($prefs['hiddenFolderIds'] ?? []);
$pathHealth = diagnosticsBuildPathHealth($type, $privacyMode);
$pathIssueCount = count($pathHealth['issues'] ?? []);
@@ -693,6 +688,7 @@ function diagnosticsBuildIntegrityChecks(string $type, array $folders, array $pr
+ count($invalidRules)
+ count($missingManualOrderIds)
+ count($missingPinnedFolderIds)
+ + count($missingHiddenFolderIds)
+ $orphanedCount
+ $buildConflicts($effectiveAssignments)['count']
+ $pathIssueCount
@@ -729,6 +725,10 @@ function diagnosticsBuildIntegrityChecks(string $type, array $folders, array $pr
'count' => count($missingPinnedFolderIds),
'ids' => array_values(array_unique($missingPinnedFolderIds))
],
+ 'missingHiddenFolderIds' => [
+ 'count' => count($missingHiddenFolderIds),
+ 'ids' => array_values(array_unique($missingHiddenFolderIds))
+ ],
'duplicateAssignments' => [
'explicit' => $buildConflicts($explicitAssignments),
'regex' => $buildConflicts($regexAssignments),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-summary.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-summary.php
index d34de629..a08ae811 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-summary.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-summary.php
@@ -1,4 +1,4 @@
- 0, 'paused' => 0, 'stopped' => 0, 'total' => 0];
$entityStateCounts = ['started' => 0, 'paused' => 0, 'stopped' => 0];
$updateCounts = ['available' => 0, 'upToDate' => 0, 'unknown' => 0, 'total' => 0];
- $assignedItemSet = [];
-
+ $assignedItemSet = []; $webuiProfileDiagnostics = fvplusMergeWebuiProfileDiagnostics([], []);
foreach ($infoByName as $name => $item) {
if (!is_array($item)) {
continue;
@@ -135,7 +134,9 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
$members[] = $name;
}
}
-
+ if ($type === 'docker') {
+ $webuiProfileDiagnostics = fvplusMergeWebuiProfileDiagnostics($webuiProfileDiagnostics, fvplusBuildWebuiProfileDiagnostics($folder['settings'] ?? [], $members));
+ }
$started = 0;
$paused = 0;
$stopped = 0;
@@ -233,7 +234,6 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
);
}
}
-
$entityDetails = [];
$entityDetailsTotal = 0;
$entityDetailsMaxEntries = 200;
@@ -306,6 +306,7 @@ function diagnosticsBuildStateSnapshot(string $type, array $folders, array $pref
'maxDepth' => $maxDepth,
'updateCounts' => $updateCounts,
'managerCounts' => $managerCounts,
+ 'webuiProfiles' => $webuiProfileDiagnostics,
'entityDetails' => [
'total' => $entityDetailsTotal,
'maxEntries' => $entityDetailsMaxEntries,
@@ -415,14 +416,12 @@ function diagnosticsBuildIntegrityIssueDetail(array $integrity): string {
);
}
- $missingPinnedFolderIds = max(0, (int)($integrity['missingPinnedFolderIds']['count'] ?? 0));
- if ($missingPinnedFolderIds > 0) {
- return sprintf(
- '%d pinned folder id%s no longer %s an existing folder.',
- $missingPinnedFolderIds,
- $missingPinnedFolderIds === 1 ? '' : 's',
- $missingPinnedFolderIds === 1 ? 'matches' : 'match'
- );
+ foreach ([['missingPinnedFolderIds', 'pinned'], ['missingHiddenFolderIds', 'hidden']] as [$key, $label]) {
+ $missingFolderIds = max(0, (int)($integrity[$key]['count'] ?? 0));
+ if ($missingFolderIds > 0) {
+ return sprintf('%d %s folder id%s no longer %s an existing folder.', $missingFolderIds, $label,
+ $missingFolderIds === 1 ? '' : 's', $missingFolderIds === 1 ? 'matches' : 'match');
+ }
}
$duplicateNameCount = max(0, (int)($integrity['duplicateFolderNames']['count'] ?? 0));
@@ -588,6 +587,7 @@ function diagnosticsBuildRecommendedActions(array $typesData, array $customIcons
|| ((int)($integrity['invalidFolderRegex']['count'] ?? 0)) > 0
|| ((int)($integrity['invalidFolderIconPaths']['count'] ?? 0)) > 0
|| ((int)($integrity['missingPinnedFolderIds']['count'] ?? 0)) > 0
+ || ((int)($integrity['missingHiddenFolderIds']['count'] ?? 0)) > 0
|| ((int)($integrity['missingManualOrderIds']['count'] ?? 0)) > 0;
if ($prefsNeedCleanup) {
break;
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-support-bundle.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-support-bundle.php
index 53ecd59b..e4aee60e 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-support-bundle.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-support-bundle.php
@@ -240,12 +240,8 @@ function diagnosticsBuildSupportBundlePluginTypeSection(string $type, array $typ
'pluginState.' . $type . '.prefs.expandedFolderState',
is_array($typeData['expandedFolderState'] ?? null) ? $typeData['expandedFolderState'] : []
),
- 'pinnedFolders' => diagnosticsSupportBundleRedactFolderIdList(
- $redactor,
- $type,
- 'pluginState.' . $type . '.prefs.pinnedFolders.*',
- is_array($typeData['pinnedFolderIds'] ?? null) ? $typeData['pinnedFolderIds'] : []
- ),
+ 'pinnedFolders' => diagnosticsSupportBundleRedactFolderIdList($redactor, $type, 'pluginState.' . $type . '.prefs.pinnedFolders.*', is_array($typeData['pinnedFolderIds'] ?? null) ? $typeData['pinnedFolderIds'] : []),
+ 'hiddenFolders' => diagnosticsSupportBundleRedactFolderIdList($redactor, $type, 'pluginState.' . $type . '.prefs.hiddenFolders.*', is_array($typeData['hiddenFolderIds'] ?? null) ? $typeData['hiddenFolderIds'] : []),
'hideEmptyFolders' => (bool)($typeData['hideEmptyFolders'] ?? false),
'appColumnWidth' => (string)($typeData['appColumnWidth'] ?? 'standard'),
'setupWizardCompleted' => (bool)($typeData['setupWizardCompleted'] ?? false),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics.php
index ee46244b..1f826ccc 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics.php
@@ -158,8 +158,8 @@ function getDiagnosticsSnapshot(string $privacyMode = FVPLUS_DIAGNOSTICS_DEFAULT
'sortMode' => $prefs['sortMode'] ?? 'created',
'ruleCount' => count($prefs['autoRules'] ?? []),
'manualOrderCount' => count($prefs['manualOrder'] ?? []),
- 'pinnedFolderCount' => count($prefs['pinnedFolderIds'] ?? []),
- 'pinnedFolderIds' => normalizeStringIdList($prefs['pinnedFolderIds'] ?? []),
+ 'pinnedFolderCount' => count($prefs['pinnedFolderIds'] ?? []), 'pinnedFolderIds' => normalizeStringIdList($prefs['pinnedFolderIds'] ?? []),
+ 'hiddenFolderCount' => count($prefs['hiddenFolderIds'] ?? []), 'hiddenFolderIds' => normalizeStringIdList($prefs['hiddenFolderIds'] ?? []),
'expandedFolderState' => normalizeExpandedStateMap($prefs['expandedFolderState'] ?? []),
'hideEmptyFolders' => normalizeBool($prefs['hideEmptyFolders'] ?? false, false),
'appColumnWidth' => normalizeAppColumnWidth($prefs['appColumnWidth'] ?? 'standard'),
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.folder-mutations.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.folder-mutations.php
index f21fdeb0..ddaafe27 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.folder-mutations.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.folder-mutations.php
@@ -264,9 +264,7 @@ function applyFolderBatchOperations(string $type, array $operations, $expectedRe
];
});
}
-
- function applyFolderMemberIdentityPatches(string $type, array $patches): array {
- $type = ensureType($type);
+ function applyFolderMemberIdentityPatches(string $type, array $patches): array { $type = ensureType($type);
$fileData = readRawFolderMap($type);
$changedFolderIds = [];
$renameCount = 0;
@@ -306,6 +304,9 @@ function applyFolderMemberIdentityPatches(string $type, array $patches): array {
}
}
unset($action);
+ $profiles = fvplusNormalizeWebuiProfiles($folder['settings']['webui_profiles'] ?? []);
+ foreach ($profiles as &$profile) $profile['containers'] = array_values(array_unique(array_map(static fn($name) => $name === $oldName ? $newName : $name, $profile['containers'])));
+ unset($profile); $folder['settings']['webui_profiles'] = $profiles;
if (is_array($folder['memberIdentities'][$oldName] ?? null)) {
$folder['memberIdentities'][$newName] = $folder['memberIdentities'][$oldName];
unset($folder['memberIdentities'][$oldName]);
@@ -356,7 +357,6 @@ function applyFolderMemberIdentityPatches(string $type, array $patches): array {
'metadata' => readConfigMetadata($type, true)
];
}
-
function applyFolderSettingsPayload(string $type, array $targetIds, array $settingsPayload): array {
$type = ensureType($type);
$normalizedSettings = normalizeFolderSettingsTransferPayload($settingsPayload);
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.php
index d165678c..f0649c57 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.php
@@ -399,9 +399,10 @@ function fv3_get_tailscale_fqdn_from_container(string $containerName, bool $cont
const FVPLUS_MAX_FOLDER_STRING_BYTES = 2048;
const FVPLUS_DOCKER_FOLDER_LABEL_KEYS = ['folderview.plus', 'folder.view3', 'folder.view2', 'folder.view'];
const FVPLUS_DEFAULT_FOLDER_STATUS_COLORS = [
- 'started' => '#ffffff',
+ 'started' => '#55b72d',
'paused' => '#b8860b',
- 'stopped' => '#ff4d4d'
+ 'stopped' => '#ff4d4d',
+ 'text' => '#ffffff'
];
const FVPLUS_LEGACY_CONFIG_DIRS = [
'/boot/config/plugins/folder.view3',
@@ -444,6 +445,7 @@ function fv3_get_tailscale_fqdn_from_container(string $containerName, bool $cont
require_once(__DIR__ . '/lib.api-contract.php');
require_once(__DIR__ . '/lib.preflight.php');
+ require_once(__DIR__ . '/lib.webui-profiles.php');
require_once(__DIR__ . '/lib.prefs.php');
require_once(__DIR__ . '/lib.diagnostics.php');
require_once(__DIR__ . '/lib.docker-runtime.php');
@@ -1198,6 +1200,10 @@ function normalizeFolderContentPayload(array $content): array {
if (!is_array($normalized['settings'] ?? null)) {
$normalized['settings'] = [];
}
+ $normalized['settings']['webui_profiles'] = fvplusNormalizeWebuiProfiles(
+ $normalized['settings']['webui_profiles'] ?? ($normalized['settings']['webuiProfiles'] ?? [])
+ );
+ unset($normalized['settings']['webuiProfiles']);
$rawPreviewRows = $normalized['settings']['preview_rows']
?? ($normalized['settings']['previewRows']
?? ($normalized['preview_rows']
@@ -1330,7 +1336,7 @@ function normalizeFolderSettingsTransferPayload(array $payload): array {
$normalized['settings'] = [];
if (array_key_exists('settings', $payload) && is_array($payload['settings'])) {
$settingsCarrier = normalizeFolderContentPayload(['settings' => $payload['settings']]);
- $normalized['settings'] = is_array($settingsCarrier['settings'] ?? null) ? $settingsCarrier['settings'] : [];
+ $normalized['settings'] = fvplusStripWebuiProfilesFromSettings($settingsCarrier['settings'] ?? []);
}
$normalizedActions = [];
@@ -1800,8 +1806,15 @@ function reorderFolderMapByPrefs(string $type, array $folders): array {
}
function reconcileManualOrderPrefs(array $prefs, array $folders): array {
+ $nextPrefs = $prefs;
+ $nextPrefs['hiddenFolderIds'] = array_values(array_filter(
+ normalizeStringIdList($prefs['hiddenFolderIds'] ?? []),
+ static function ($id) use ($folders): bool {
+ return array_key_exists((string)$id, $folders);
+ }
+ ));
if (($prefs['sortMode'] ?? 'created') !== 'manual') {
- return $prefs;
+ return $nextPrefs;
}
$order = [];
foreach ((array)($prefs['manualOrder'] ?? []) as $id) {
@@ -1814,8 +1827,8 @@ function reconcileManualOrderPrefs(array $prefs, array $folders): array {
$order[] = $id;
}
}
- $prefs['manualOrder'] = $order;
- return $prefs;
+ $nextPrefs['manualOrder'] = $order;
+ return $nextPrefs;
}
function syncManualOrderWithFolders(string $type, array $folders): void {
@@ -1998,7 +2011,7 @@ function normalizeTemplateEntry(array $template): ?array {
if ($id === '') {
$id = generateId(12);
}
- $settings = is_array($template['settings'] ?? null) ? $template['settings'] : [];
+ $settings = fvplusStripWebuiProfilesFromSettings($template['settings'] ?? []);
$actions = is_array($template['actions'] ?? null) ? array_values($template['actions']) : [];
return [
'id' => $id,
@@ -2079,7 +2092,7 @@ function createFolderTemplateFromFolder(string $type, string $folderId, string $
'name' => $name,
'icon' => (string)($folder['icon'] ?? ''),
'regex' => (string)($folder['regex'] ?? ''),
- 'settings' => is_array($folder['settings'] ?? null) ? $folder['settings'] : [],
+ 'settings' => fvplusStripWebuiProfilesFromSettings($folder['settings'] ?? []),
'actions' => is_array($folder['actions'] ?? null) ? $folder['actions'] : [],
'createdAt' => gmdate('c')
];
@@ -2554,7 +2567,6 @@ function autoRuleDecision(array $rules, string $name, array $infoByName, string
}
require_once(__DIR__ . '/lib.folder-rules.php'); require_once(__DIR__ . '/lib.docker-start-order-sequence.php');
-
require_once(__DIR__ . '/lib.docker-order.php');
require_once(__DIR__ . '/lib.folder-mutations.php');
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.prefs.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.prefs.php
index c7a939fc..f40d84f0 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.prefs.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.prefs.php
@@ -9,6 +9,7 @@ function defaultTypePrefs(): array {
'sortMode' => 'created',
'manualOrder' => [],
'pinnedFolderIds' => [],
+ 'hiddenFolderIds' => [],
'expandedFolderState' => [],
'hideEmptyFolders' => false,
'appColumnWidth' => 'standard',
@@ -108,6 +109,7 @@ function normalizeTypeFolderDefaultsProfile($value): array {
$incoming = is_array($value) ? $value : [];
$profileIncoming = is_array($incoming['profile'] ?? null) ? $incoming['profile'] : [];
$settingsIncoming = is_array($profileIncoming['settings'] ?? null) ? $profileIncoming['settings'] : [];
+ $settingsIncoming = fvplusStripWebuiProfilesFromSettings($settingsIncoming);
$actionsIncoming = is_array($profileIncoming['actions'] ?? null) ? $profileIncoming['actions'] : [];
$normalizedActions = [];
@@ -442,6 +444,7 @@ function normalizeTypePrefs(array $prefs): array {
}
$normalized['manualOrder'] = normalizeStringIdList($manualOrder);
$normalized['pinnedFolderIds'] = normalizeStringIdList($prefs['pinnedFolderIds'] ?? []);
+ $normalized['hiddenFolderIds'] = normalizeStringIdList($prefs['hiddenFolderIds'] ?? []);
$normalized['expandedFolderState'] = normalizeExpandedStateMap($prefs['expandedFolderState'] ?? []);
$normalized['hideEmptyFolders'] = normalizeBool($prefs['hideEmptyFolders'] ?? false, false);
$normalized['appColumnWidth'] = normalizeAppColumnWidth($prefs['appColumnWidth'] ?? 'standard');
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.validation.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.validation.php
index 04a3f326..1b15fef5 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.validation.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.validation.php
@@ -152,6 +152,9 @@ function fvplus_assert_prefs_payload_shape(array $payload): void {
if (array_key_exists('pinnedFolderIds', $payload)) {
fvplus_validation_assert_list_of_scalarish($payload['pinnedFolderIds'], 'pinnedFolderIds', 10000);
}
+ if (array_key_exists('hiddenFolderIds', $payload)) {
+ fvplus_validation_assert_list_of_scalarish($payload['hiddenFolderIds'], 'hiddenFolderIds', 10000);
+ }
if (array_key_exists('expandedFolderState', $payload)) {
fvplus_validation_assert_assoc_map($payload['expandedFolderState'], 'expandedFolderState', 20000);
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.webui-profiles.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.webui-profiles.php
new file mode 100644
index 00000000..403a2c39
--- /dev/null
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.webui-profiles.php
@@ -0,0 +1,127 @@
+ $rawProfile) {
+ if (count($profiles) >= FVPLUS_WEBUI_PROFILE_MAX_PROFILES || !is_array($rawProfile)) {
+ continue;
+ }
+ $id = fvplusWebuiProfileCleanString($rawProfile['id'] ?? '', 64);
+ if (!preg_match('/^[A-Za-z0-9._:-]{1,64}$/', $id)) {
+ $id = 'profile-' . ((int)$sourceIndex + 1);
+ }
+ $baseId = $id;
+ $suffix = 2;
+ while (isset($usedIds[$id])) {
+ $id = substr($baseId, 0, 58) . '-' . $suffix;
+ $suffix++;
+ }
+ $usedIds[$id] = true;
+
+ $name = fvplusWebuiProfileCleanString($rawProfile['name'] ?? '', FVPLUS_WEBUI_PROFILE_MAX_NAME_BYTES);
+ if ($name === '') {
+ $name = 'Profile ' . (count($profiles) + 1);
+ }
+ $baseName = $name;
+ $nameKey = strtolower($name);
+ $suffix = 2;
+ while (isset($usedNames[$nameKey])) {
+ $suffixText = ' ' . $suffix;
+ $name = substr($baseName, 0, max(1, FVPLUS_WEBUI_PROFILE_MAX_NAME_BYTES - strlen($suffixText))) . $suffixText;
+ $nameKey = strtolower($name);
+ $suffix++;
+ }
+ $usedNames[$nameKey] = true;
+
+ $rawMembers = is_array($rawProfile['containers'] ?? null)
+ ? $rawProfile['containers']
+ : (is_array($rawProfile['members'] ?? null) ? $rawProfile['members'] : []);
+ $containers = [];
+ $seenMembers = [];
+ foreach ($rawMembers as $rawMember) {
+ if (count($containers) >= FVPLUS_WEBUI_PROFILE_MAX_MEMBERS) {
+ break;
+ }
+ $member = fvplusWebuiProfileCleanString($rawMember, FVPLUS_WEBUI_PROFILE_MAX_MEMBER_BYTES);
+ if ($member === '' || isset($seenMembers[$member])) {
+ continue;
+ }
+ $seenMembers[$member] = true;
+ $containers[] = $member;
+ }
+ $profiles[] = ['id' => $id, 'name' => $name, 'containers' => $containers];
+ }
+ return $profiles;
+}
+
+function fvplusStripWebuiProfilesFromSettings($settings): array {
+ $normalized = is_array($settings) ? $settings : [];
+ unset($normalized['webui_profiles'], $normalized['webuiProfiles']);
+ return $normalized;
+}
+
+function fvplusBuildWebuiProfileDiagnostics($settings, array $availableMembers): array {
+ $rawSettings = is_array($settings) ? $settings : [];
+ $rawProfiles = $rawSettings['webui_profiles'] ?? ($rawSettings['webuiProfiles'] ?? []);
+ $invalidProfileCount = 0;
+ $rawIds = [];
+ $duplicateProfileIdCount = 0;
+ foreach (is_array($rawProfiles) ? $rawProfiles : [] as $rawProfile) {
+ if (!is_array($rawProfile)) {
+ $invalidProfileCount++;
+ continue;
+ }
+ $rawId = fvplusWebuiProfileCleanString($rawProfile['id'] ?? '', 64);
+ $rawName = fvplusWebuiProfileCleanString($rawProfile['name'] ?? '', FVPLUS_WEBUI_PROFILE_MAX_NAME_BYTES);
+ if ($rawId === '' || !preg_match('/^[A-Za-z0-9._:-]{1,64}$/', $rawId) || $rawName === '') {
+ $invalidProfileCount++;
+ }
+ if ($rawId !== '' && isset($rawIds[$rawId])) {
+ $duplicateProfileIdCount++;
+ }
+ if ($rawId !== '') {
+ $rawIds[$rawId] = true;
+ }
+ }
+ $profiles = fvplusNormalizeWebuiProfiles($rawProfiles);
+ $available = array_fill_keys(array_values(array_unique(array_map('strval', $availableMembers))), true);
+ $selectedReferenceCount = 0;
+ $unavailableReferenceCount = 0;
+ foreach ($profiles as $profile) {
+ foreach ($profile['containers'] as $member) {
+ $selectedReferenceCount++;
+ if (!isset($available[$member])) {
+ $unavailableReferenceCount++;
+ }
+ }
+ }
+ return [
+ 'profileCount' => count($profiles),
+ 'selectedReferenceCount' => $selectedReferenceCount,
+ 'unavailableReferenceCount' => $unavailableReferenceCount,
+ 'duplicateProfileIdCount' => $duplicateProfileIdCount,
+ 'invalidProfileCount' => $invalidProfileCount
+ ];
+}
+
+function fvplusMergeWebuiProfileDiagnostics(array $left, array $right): array {
+ $keys = ['profileCount', 'selectedReferenceCount', 'unavailableReferenceCount', 'duplicateProfileIdCount', 'invalidProfileCount'];
+ $merged = [];
+ foreach ($keys as $key) {
+ $merged[$key] = max(0, (int)($left[$key] ?? 0)) + max(0, (int)($right[$key] ?? 0));
+ }
+ return $merged;
+}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/prefs.php b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/prefs.php
index 973c995b..a49abc1a 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/prefs.php
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/prefs.php
@@ -47,6 +47,7 @@
(string)($current['sortMode'] ?? 'created') !== (string)($saved['sortMode'] ?? 'created')
|| normalizeStringIdList($current['manualOrder'] ?? []) !== normalizeStringIdList($saved['manualOrder'] ?? [])
|| normalizeStringIdList($current['pinnedFolderIds'] ?? []) !== normalizeStringIdList($saved['pinnedFolderIds'] ?? [])
+ || normalizeStringIdList($current['hiddenFolderIds'] ?? []) !== normalizeStringIdList($saved['hiddenFolderIds'] ?? [])
);
if ($orderPrefsChanged) {
syncManualOrderWithFolders($type, readRawFolderMap($type));
@@ -75,7 +76,8 @@
'backupCoalesced' => (bool)($backup['coalesced'] ?? false),
'sortMode' => (string)($saved['sortMode'] ?? 'created'),
'ruleCount' => count($saved['autoRules'] ?? []),
- 'pinnedFolderCount' => count($saved['pinnedFolderIds'] ?? [])
+ 'pinnedFolderCount' => count($saved['pinnedFolderIds'] ?? []),
+ 'hiddenFolderCount' => count($saved['hiddenFolderIds'] ?? [])
], 'ok', 'server');
$auditRecorded = true;
} catch (Throwable $err) {
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css
index 0d75a4cc..2b0b75b5 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css
@@ -550,12 +550,45 @@ tr.fv-folder-locked .folder-appname {
display: none !important;
}
+#docker_list > tr.fv-folder-user-hidden {
+ display: none !important;
+}
+
+body.fvplus-docker-reveal-hidden #docker_list > tr.fv-folder-user-hidden:not(.fv-toolbar-filter-hidden):not(.fv-folder-focus-hidden) {
+ display: table-row !important;
+ opacity: 0.58;
+}
+
+body.fvplus-docker-reveal-hidden #docker_list > tr.folder.fv-folder-hidden-revealed > td {
+ background-image: linear-gradient(rgba(255, 154, 60, 0.045), rgba(255, 154, 60, 0.045));
+}
+
+.fv-folder-hidden-indicator {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.24rem;
+ margin-left: 0.45rem;
+ color: #ff9a3c;
+ font-size: 0.86em;
+ font-weight: 700;
+ text-transform: uppercase;
+}
+
+.fv-folder-hidden-indicator-icon,
+.fvplus-docker-quick-svg,
+.fvplus-docker-action-svg,
+.fvplus-docker-visibility-notice-icon {
+ width: 1em;
+ height: 1em;
+ flex: 0 0 auto;
+}
+
.folder-update-text {
white-space: nowrap;
}
span.folder-state.fv-folder-state-started {
- color: var(--fvplus-folder-status-started, var(--fvplus-status-started));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
span.folder-state {
@@ -565,11 +598,11 @@ span.folder-state {
}
span.folder-state.fv-folder-state-paused {
- color: var(--fvplus-folder-status-paused, var(--fvplus-status-paused));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
span.folder-state.fv-folder-state-stopped {
- color: var(--fvplus-folder-status-stopped, var(--fvplus-status-stopped));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
i.folder-load-status.started {
@@ -973,6 +1006,29 @@ tr.folder.fv-parent-expanded div.folder-preview {
color: var(--fvplus-folder-status-stopped, var(--fvplus-status-stopped)) !important;
}
+.folder-preview .fv-preview-status-compact .state,
+.folder-preview span.state[class*="fv-preview-status-"] {
+ color: var(--fvplus-folder-status-text, #ffffff) !important;
+}
+
+.folder-preview i.fv-preview-lifecycle-pending {
+ color: var(--fvplus-folder-status-started, var(--fvplus-status-started)) !important;
+}
+
+.folder-preview [data-fv-lifecycle-action="start"] i.fv-preview-lifecycle-pending,
+.folder-preview [data-fv-lifecycle-action="resume"] i.fv-preview-lifecycle-pending,
+.folder-preview [data-fv-lifecycle-action="restart"] i.fv-preview-lifecycle-pending {
+ color: var(--fvplus-folder-status-started, var(--fvplus-status-started)) !important;
+}
+
+.folder-preview [data-fv-lifecycle-action="pause"] i.fv-preview-lifecycle-pending {
+ color: var(--fvplus-folder-status-paused, var(--fvplus-status-paused)) !important;
+}
+
+.folder-preview [data-fv-lifecycle-action="stop"] i.fv-preview-lifecycle-pending {
+ color: var(--fvplus-folder-status-stopped, var(--fvplus-status-stopped)) !important;
+}
+
.folder-preview-wrapper .inner > span.appname {
display: inline-block;
overflow: clip;
@@ -1391,6 +1447,12 @@ div.folder-preview-4 span.outer {
font-size: 14px !important;
}
+.fvplus-docker-context-menu > li.fvplus-docker-quick-item .fvplus-docker-quick-svg {
+ width: 15px;
+ height: 15px;
+ font-size: 15px !important;
+}
+
.fvplus-docker-context-menu > li.fvplus-docker-quick-clear {
clear: both;
}
@@ -1688,6 +1750,59 @@ body.fvplus-privacy-vm-sensitive-externalUrls a.fvplus-sensitive-external-url {
background: rgba(128, 128, 128, 0.32);
}
+.fvplus-docker-action-menu-item .fvplus-docker-action-svg {
+ justify-self: center;
+}
+
+.fvplus-docker-visibility-notice {
+ flex: 1 0 100%;
+ width: 100%;
+ min-width: 0;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ gap: 0.55rem;
+ padding: 0.58rem 0.72rem;
+ border: 1px solid rgba(255, 154, 60, 0.48);
+ border-radius: 6px;
+ background: rgba(255, 154, 60, 0.08);
+ color: inherit;
+}
+
+.fvplus-docker-visibility-notice > span {
+ flex: 1 1 auto;
+ min-width: 0;
+}
+
+.fvplus-docker-visibility-notice > button {
+ flex: 0 0 auto;
+ margin: 0 !important;
+}
+
+.fvplus-docker-visibility-notice > .fvplus-docker-visibility-button {
+ border: 1px solid var(--fvplus-ui-border-subtle, rgba(128, 128, 128, 0.34)) !important;
+ background: var(--fvplus-ui-control, rgba(128, 128, 128, 0.1)) !important;
+ color: var(--fvplus-ui-text-primary, inherit) !important;
+ box-shadow: none !important;
+}
+
+.fvplus-docker-visibility-notice > .fvplus-docker-visibility-button:hover:not(:disabled) {
+ border-color: var(--fvplus-ui-border-strong, rgba(128, 128, 128, 0.58)) !important;
+ background: var(--fvplus-ui-control-hover, rgba(128, 128, 128, 0.2)) !important;
+ color: var(--fvplus-ui-text-primary, inherit) !important;
+}
+
+.fvplus-docker-visibility-notice > .fvplus-docker-visibility-button:focus-visible {
+ border-color: var(--fvplus-ui-border-strong, rgba(128, 128, 128, 0.58)) !important;
+ box-shadow: 0 0 0 3px var(--fvplus-ui-focus-ring, rgba(74, 179, 255, 0.24)) !important;
+ outline: 0 !important;
+}
+
+.fvplus-docker-visibility-notice.is-undo {
+ border-color: rgba(85, 183, 45, 0.52);
+ background: rgba(85, 183, 45, 0.08);
+}
+
#docker_list > tr.fv-toolbar-filter-hidden {
display: none !important;
}
@@ -1727,4 +1842,13 @@ body.fvplus-privacy-vm-sensitive-externalUrls a.fvplus-sensitive-external-url {
.fvplus-docker-action-separator {
display: none;
}
+
+ .fvplus-docker-visibility-notice {
+ align-items: stretch;
+ flex-wrap: wrap;
+ }
+
+ .fvplus-docker-visibility-notice > span {
+ flex-basis: calc(100% - 2rem);
+ }
}
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folder.css b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folder.css
index 06c5116a..b882c164 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folder.css
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folder.css
@@ -4954,7 +4954,8 @@ table.sortable td:nth-child(4) {
.fv-third-party-pack-actions-panel > button,
.fv-third-party-inline-actions > button,
.fv-modern-field-row .custom-action,
- .fv-folder-action-dialog .ui-dialog-buttonpane button
+ .fv-folder-action-dialog .ui-dialog-buttonpane button,
+ .fv-webui-profile-button
) {
border: 0 !important;
border-radius: 8px !important;
@@ -4982,7 +4983,8 @@ table.sortable td:nth-child(4) {
.fv-third-party-pack-actions-panel > button,
.fv-third-party-inline-actions > button,
.fv-modern-field-row .custom-action,
- .fv-folder-action-dialog .ui-dialog-buttonpane button
+ .fv-folder-action-dialog .ui-dialog-buttonpane button,
+ .fv-webui-profile-button
):hover,
:is(
.fv-editor-hero-actions > button,
@@ -5001,7 +5003,8 @@ table.sortable td:nth-child(4) {
.fv-third-party-pack-actions-panel > button,
.fv-third-party-inline-actions > button,
.fv-modern-field-row .custom-action,
- .fv-folder-action-dialog .ui-dialog-buttonpane button
+ .fv-folder-action-dialog .ui-dialog-buttonpane button,
+ .fv-webui-profile-button
):focus-visible {
background: var(--fv-editor-button-hover-top) !important;
box-shadow: var(--fv-editor-button-shadow-hover) !important;
@@ -5024,7 +5027,8 @@ table.sortable td:nth-child(4) {
.fv-third-party-pack-actions-panel > button,
.fv-third-party-inline-actions > button,
.fv-modern-field-row .custom-action,
- .fv-folder-action-dialog .ui-dialog-buttonpane button
+ .fv-folder-action-dialog .ui-dialog-buttonpane button,
+ .fv-webui-profile-button
):active {
background: var(--fv-editor-button-active-top) !important;
box-shadow: var(--fv-editor-button-shadow-active) !important;
@@ -5295,6 +5299,130 @@ body[data-fvplus-host-theme="white"] #fvEditorActionBar .folder-btn-submit:activ
display: none !important;
}
+.fv-webui-profiles-section dl,
+.fv-webui-profiles-section dd {
+ width: 100%;
+}
+
+#fvWebuiProfilesWorkspace {
+ display: grid;
+ gap: 0.85rem;
+ width: 100%;
+}
+
+.fv-webui-profiles-toolbar,
+.fv-webui-profile-head,
+.fv-webui-profile-tools,
+.fv-webui-profile-member {
+ display: flex;
+ align-items: center;
+ gap: 0.65rem;
+}
+
+.fv-webui-profiles-toolbar,
+.fv-webui-profile-head {
+ justify-content: space-between;
+}
+
+.fv-webui-profiles-toolbar p,
+.fv-webui-profile-card p {
+ margin: 0;
+}
+
+.fv-webui-profile-list {
+ display: grid;
+ gap: 0.85rem;
+}
+
+.fv-webui-profile-card {
+ display: grid;
+ gap: 0.75rem;
+ padding: 0.9rem;
+ border: 1px solid var(--fv-editor-border, rgba(127, 127, 127, 0.35));
+ border-radius: 8px;
+ background: var(--fv-editor-panel, rgba(127, 127, 127, 0.06));
+}
+
+.fv-webui-profile-name,
+.fv-webui-profile-tools input[type="search"] {
+ min-width: 0;
+ border-color: var(--fv-editor-border, currentColor);
+ background: var(--fv-editor-input-bg, inherit);
+ color: inherit;
+}
+
+.fv-webui-profile-name {
+ flex: 1 1 18rem;
+ font-weight: 700;
+}
+
+.fv-webui-profile-actions,
+.fv-webui-profile-tools {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.45rem;
+}
+
+.fv-webui-profile-tools input[type="search"] {
+ flex: 1 1 14rem;
+}
+
+.fv-webui-profile-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.35rem;
+ min-height: 34px;
+ padding: 0.35rem 0.65rem;
+ cursor: pointer;
+}
+
+.fv-webui-profile-button:focus-visible {
+ outline: 2px solid var(--fv-editor-focus-ring) !important;
+ outline-offset: 2px;
+}
+
+.fv-webui-profile-button > i,
+.fv-webui-profile-button > span {
+ color: inherit !important;
+}
+
+.fv-webui-profile-members {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
+ gap: 0.45rem;
+}
+
+.fv-webui-profile-member {
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ padding: 0.55rem 0.65rem;
+ border: 1px solid var(--fv-editor-border, rgba(127, 127, 127, 0.3));
+ border-radius: 6px;
+ cursor: pointer;
+}
+
+.fv-webui-profile-member > span {
+ grid-column: 2;
+ opacity: 0.72;
+ font-size: 0.86rem;
+}
+
+.fv-webui-profile-member.is-unavailable {
+ border-style: dashed;
+ opacity: 0.75;
+}
+
+.fv-webui-profiles-status {
+ min-height: 1.3em;
+}
+
+.fv-webui-profiles-status.is-error,
+.fv-input-error {
+ color: var(--fv-editor-danger, #e05252);
+ border-color: var(--fv-editor-danger, #e05252) !important;
+}
+
@media (max-width: 980px) {
#fvEditorChrome {
position: static;
@@ -5378,6 +5506,23 @@ body[data-fvplus-host-theme="white"] #fvEditorActionBar .folder-btn-submit:activ
width: 100%;
}
+ .fv-webui-profiles-toolbar,
+ .fv-webui-profile-head,
+ .fv-webui-profile-tools {
+ align-items: stretch;
+ flex-direction: column;
+ }
+
+ .fv-webui-profile-actions,
+ .fv-webui-profile-button,
+ .fv-webui-profile-tools input[type="search"] {
+ width: 100%;
+ }
+
+ .fv-webui-profile-actions .fv-webui-profile-button {
+ flex: 1 1 9rem;
+ }
+
.fv-custom-icon-row {
grid-template-columns: 24px minmax(0, 1fr);
align-items: start;
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folderviewplus.css b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folderviewplus.css
index bd1014b7..3aa66a9d 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folderviewplus.css
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/folderviewplus.css
@@ -3544,7 +3544,7 @@ body.fv-mobile-compact #fv-settings-root .status-breakdown-list {
--fvplus-theme-accent: var(--fvplus-settings-accent, #f0a030);
--fvplus-theme-surface-panel: var(--fvplus-settings-surface-panel, #1b1d20);
--fvplus-theme-border-subtle: var(--fvplus-settings-border-subtle, rgba(255, 255, 255, 0.12));
- --fvplus-status-started: #ffffff;
+ --fvplus-status-started: #55b72d;
--fvplus-status-paused: #b8860b;
--fvplus-status-stopped: #ff4d4d;
--fvplus-graph-cpu: #5aa4ff;
diff --git a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/vm.css b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/vm.css
index 8b82031f..f46b13eb 100644
--- a/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/vm.css
+++ b/src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/vm.css
@@ -241,7 +241,7 @@ tr.fv-folder-locked .folder-appname {
}
span.folder-state.fv-folder-state-started {
- color: var(--fvplus-folder-status-started, var(--fvplus-status-started));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
span.folder-state {
@@ -251,11 +251,11 @@ span.folder-state {
}
span.folder-state.fv-folder-state-paused {
- color: var(--fvplus-folder-status-paused, var(--fvplus-status-paused));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
span.folder-state.fv-folder-state-stopped {
- color: var(--fvplus-folder-status-stopped, var(--fvplus-status-stopped));
+ color: var(--fvplus-folder-status-text, #ffffff);
}
i.folder-load-status.started {
diff --git a/tests/architecture-boundaries.test.mjs b/tests/architecture-boundaries.test.mjs
index aeff5787..f86b102c 100644
--- a/tests/architecture-boundaries.test.mjs
+++ b/tests/architecture-boundaries.test.mjs
@@ -15,14 +15,14 @@ test('all FolderView Plus page and loader script graphs are contract-guarded', (
assert.deepEqual(result.failures, []);
assert.equal(result.pageCount, 5);
assert.equal(result.sourceCount, 8);
- assert.equal(result.referenceCount, 290);
+ assert.equal(result.referenceCount, 296);
});
test('entrypoints and contracted modules declare ownership boundaries', () => {
assert.equal(architecture.schemaVersion, 2);
assert.equal(architecture.entrypointContracts.length, 9);
- assert.equal(architecture.moduleContracts.length, 50);
- assert.equal(architecture.serverModuleContracts.length, 29);
+ assert.equal(architecture.moduleContracts.length, 53);
+ assert.equal(architecture.serverModuleContracts.length, 30);
const allowedConsumers = new Set(architecture.consumerScopes);
for (const contract of [...architecture.entrypointContracts, ...architecture.moduleContracts, ...architecture.serverModuleContracts]) {
assert.ok(contract.file, 'boundary contract must name its file');
diff --git a/tests/browser/cases/docker-hidden-folders.mjs b/tests/browser/cases/docker-hidden-folders.mjs
new file mode 100644
index 00000000..b30e8ea7
--- /dev/null
+++ b/tests/browser/cases/docker-hidden-folders.mjs
@@ -0,0 +1,26 @@
+import assert from 'node:assert/strict';
+import { assertImmediateThemedUndo } from '../helpers/docker-hidden-folder-context.mjs';
+
+export const registerDockerHiddenFolderFixtureCases = ({ test, baseUrl }) => {
+test('Docker hidden folders remain recoverable through reveal, restore all, and undo', async ({ page }) => {
+ await page.goto(`${baseUrl}/runtime`, { waitUntil: 'load' });
+ await assertImmediateThemedUndo(page, 'media');
+
+ await page.click('[data-fvplus-docker-menu="view"]');
+ const revealControl = page.locator('#fvplus-docker-action-bar .fvplus-docker-action-menu.is-open [data-fvplus-docker-hidden="toggle-reveal"]');
+ assert.match(await revealControl.textContent(), /Hidden folders \(1\)/);
+ await revealControl.click();
+ assert.equal(await page.locator('#docker_list > tr.folder-id-media').isVisible(), true);
+ assert.equal(await page.locator('#docker_list > tr.folder-id-media .fv-folder-hidden-indicator').count(), 1);
+
+ await page.click('[data-fvplus-docker-menu="view"]');
+ await page.click('[data-fvplus-docker-hidden="restore-all"]');
+ await page.waitForFunction(() => window.fixtureRuntime.hiddenFolders.getSummary().explicitCount === 0);
+ assert.equal(await page.locator('#docker_list > tr.fv-folder-user-hidden').count(), 0);
+
+ await page.evaluate(() => window.fixtureRuntime.hiddenFolders.hideFolder('updates'));
+ await page.click('[data-fvplus-docker-hidden="undo"]');
+ await page.waitForFunction(() => window.fixtureRuntime.hiddenFolders.getSummary().explicitCount === 0);
+ assert.equal(await page.locator('#docker_list > tr.folder-id-updates').isVisible(), true);
+});
+};
diff --git a/tests/browser/cases/docker.mjs b/tests/browser/cases/docker.mjs
index 921808bb..1cc7ce40 100644
--- a/tests/browser/cases/docker.mjs
+++ b/tests/browser/cases/docker.mjs
@@ -1,5 +1,8 @@
import assert from 'node:assert/strict';
-import { exerciseDockerPreviewContextDiagnostics } from '../helpers/docker-preview-context.mjs';
+import {
+ exerciseChildFolderPreviewContext,
+ exerciseDockerPreviewContextDiagnostics
+} from '../helpers/docker-preview-context.mjs';
export const registerDockerFixtureCases = ({ test, baseUrl }) => {
test('Docker action bar is idempotent and reports fixture counts', async ({ page }) => {
@@ -61,13 +64,29 @@ test('Docker single-row preview cloning falls back without stopping later member
assert.deepEqual(result.missingWrapper, { ok: false, reason: 'preview-markup-missing' });
assert.deepEqual(result.invalidSelector, { ok: false, reason: 'preview-clone-failed' });
assert.equal(result.renderedCount, 3, 'a malformed member must not stop subsequent preview rendering');
+ assert.equal(result.renderedContainerId, 'fixture-0');
+ assert.equal(result.renderedContainerName, 'fixture-container-0');
assert.equal(result.fallbackCount, 2);
});
+test('Docker lifecycle pending colors follow each action destination state', async ({ page }) => {
+ await page.goto(`${baseUrl}/docker-layout-stability`, { waitUntil: 'load' });
+ const colors = await page.evaluate(() => window.fixtureDockerLifecyclePendingColors.run());
+ assert.equal(colors.start, 'rgb(17, 170, 34)');
+ assert.equal(colors.resume, 'rgb(17, 170, 34)');
+ assert.equal(colors.restart, 'rgb(17, 170, 34)');
+ assert.equal(colors.pause, 'rgb(204, 153, 0)');
+ assert.equal(colors.stop, 'rgb(221, 34, 51)');
+});
+
test('Docker multi-row previews bridge native context without cloned handlers or duplicate ids', async ({ page }) => {
await exerciseDockerPreviewContextDiagnostics({ page, baseUrl });
});
+test('Docker child-folder preview chip opens its action menu after multi-row reflow', async ({ page }) => {
+ await exerciseChildFolderPreviewContext({ page, baseUrl });
+});
+
test('Docker folder context menu opens from the first folder-icon click', async ({ page }) => {
await page.goto(`${baseUrl}/docker-layout-stability`, { waitUntil: 'load' });
const result = await page.evaluate(() => window.fixtureFolderContextFirstClick.run());
diff --git a/tests/browser/cases/folder-editor.mjs b/tests/browser/cases/folder-editor.mjs
index 3d3e418c..08ce281f 100644
--- a/tests/browser/cases/folder-editor.mjs
+++ b/tests/browser/cases/folder-editor.mjs
@@ -1,5 +1,4 @@
import assert from 'node:assert/strict';
-
export const registerFolderEditorFixtureCases = ({ test, baseUrl }) => {
test('Folder action sheet uses the compact retained action set and accessible dialog', async ({ page }) => {
await page.goto(`${baseUrl}/folder-editor`, { waitUntil: 'load' });
@@ -41,4 +40,5 @@ test('Folder action sheet traps focus, copies details, and restores trigger focu
assert.equal(await page.evaluate(() => window.fixtureFolderEditor.calls.includes('delete')), true);
assert.equal(await page.locator('#fv-folder-action-sheet-backdrop').count(), 0);
});
+
};
diff --git a/tests/browser/cases/folder-webui-profile-theme.mjs b/tests/browser/cases/folder-webui-profile-theme.mjs
new file mode 100644
index 00000000..cd507a66
--- /dev/null
+++ b/tests/browser/cases/folder-webui-profile-theme.mjs
@@ -0,0 +1,37 @@
+import assert from 'node:assert/strict';
+export const registerFolderWebuiProfileThemeFixtureCase = ({ test, baseUrl }) => {
+test('Custom WebUI profile buttons resist Unraid defaults and follow editor theme tokens', async ({ page }) => {
+ await page.goto(`${baseUrl}/folder-editor`, { waitUntil: 'load' });
+ await page.getByRole('button', { name: 'Add profile' }).click();
+ const readTheme = () => page.evaluate(() => {
+ const form = document.querySelector('.folder-editor-form');
+ const neutral = document.querySelector('.fv-webui-profile-actions .fv-webui-profile-button:not(.is-danger)');
+ const neutralProbe = document.createElement('span');
+ neutralProbe.style.cssText = 'position:absolute;left:-9999px;background:var(--fv-editor-button-bg-top);color:var(--fv-editor-button-fg)';
+ form.append(neutralProbe);
+ const neutralStyle = getComputedStyle(neutral);
+ const neutralProbeStyle = getComputedStyle(neutralProbe);
+ const result = {
+ background: neutralStyle.backgroundColor, expectedBackground: neutralProbeStyle.backgroundColor,
+ color: neutralStyle.color, expectedColor: neutralProbeStyle.color,
+ borderWidth: neutralStyle.borderTopWidth
+ };
+ neutralProbe.remove();
+ return result;
+ });
+ const dark = await readTheme();
+ assert.equal(dark.background, dark.expectedBackground);
+ assert.equal(dark.color, dark.expectedColor);
+ assert.equal(dark.borderWidth, '0px');
+ assert.notEqual(dark.color, 'rgb(255, 128, 0)');
+ await page.evaluate(() => { document.documentElement.dataset.fvplusHostTheme = 'white'; });
+ await page.waitForTimeout(200);
+ const light = await readTheme();
+ assert.equal(light.background, light.expectedBackground);
+ assert.equal(light.color, light.expectedColor);
+ assert.equal(light.borderWidth, '0px');
+ assert.notEqual(light.background, dark.background);
+ await page.evaluate(() => { delete document.documentElement.dataset.fvplusHostTheme; });
+ await page.waitForTimeout(200);
+});
+};
diff --git a/tests/browser/cases/folder-webui-profiles.mjs b/tests/browser/cases/folder-webui-profiles.mjs
new file mode 100644
index 00000000..0d272bb9
--- /dev/null
+++ b/tests/browser/cases/folder-webui-profiles.mjs
@@ -0,0 +1,32 @@
+import assert from 'node:assert/strict';
+
+export const registerFolderWebuiProfileFixtureCases = ({ test, baseUrl }) => {
+test('Custom WebUI profiles support create, select, duplicate, reorder, and delete', async ({ page }) => {
+ await page.goto(`${baseUrl}/folder-editor`, { waitUntil: 'load' });
+ await page.getByRole('button', { name: 'Add profile' }).click();
+ const card = page.locator('.fv-webui-profile-card').first();
+ await card.locator('.fv-webui-profile-name').fill('Media tools');
+ await card.getByText('Plex', { exact: true }).click();
+ await card.getByText('Tautulli', { exact: true }).click();
+ let stored = JSON.parse(await page.locator('input[name="webui_profiles"]').inputValue());
+ assert.deepEqual(stored[0].containers.sort(), ['Plex', 'Tautulli']);
+ await card.getByRole('button', { name: 'Duplicate' }).click();
+ assert.equal(await page.locator('.fv-webui-profile-card').count(), 2);
+ await page.locator('.fv-webui-profile-card').nth(1).getByRole('button', { name: 'Move up' }).click();
+ await page.locator('.fv-webui-profile-card').first().getByRole('button', { name: 'Delete' }).click();
+ assert.equal(await page.locator('.fv-webui-profile-card').count(), 1);
+ stored = JSON.parse(await page.locator('input[name="webui_profiles"]').inputValue());
+ assert.equal(stored[0].name, 'Media tools');
+});
+
+test('Custom WebUI profile editor remains usable on mobile and keyboard focus is visible', async ({ page }) => {
+ await page.setViewportSize({ width: 390, height: 844 });
+ await page.goto(`${baseUrl}/folder-editor`, { waitUntil: 'load' });
+ await page.getByRole('button', { name: 'Add profile' }).click();
+ const overflow = await page.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth);
+ assert.ok(overflow <= 1, `unexpected horizontal overflow: ${overflow}px`);
+ await page.keyboard.press('Tab');
+ const focusedTag = await page.evaluate(() => document.activeElement?.tagName || '');
+ assert.ok(['BUTTON', 'INPUT'].includes(focusedTag));
+});
+};
diff --git a/tests/browser/cases/foundation.mjs b/tests/browser/cases/foundation.mjs
index be030b91..648a5bf0 100644
--- a/tests/browser/cases/foundation.mjs
+++ b/tests/browser/cases/foundation.mjs
@@ -121,7 +121,7 @@ test('Generated localization covers initial, attributed, parameterized, and dyna
await page.waitForTimeout(150);
const settledSnapshot = await page.evaluate(() => window.FolderViewPlusI18n.snapshot());
assert.equal(snapshot.dynamicTranslationObserver, true);
- assert.equal(snapshot.autoBoundMessageCount, 1574);
+ assert.equal(snapshot.autoBoundMessageCount, 1579);
assert.ok(snapshot.autoTranslatedNodeCount >= 303);
assert.equal(settledSnapshot.autoTranslatedNodeCount, snapshot.autoTranslatedNodeCount, 'localization must settle without observing its own writes forever');
});
diff --git a/tests/browser/fixtures/docker-layout-stability.html b/tests/browser/fixtures/docker-layout-stability.html
index e76819ef..e0a6ef29 100644
--- a/tests/browser/fixtures/docker-layout-stability.html
+++ b/tests/browser/fixtures/docker-layout-stability.html
@@ -94,10 +94,23 @@
+
+
+
+ |
+
+
+ |
+
+ | Child folder row |
+
+
+
+
@@ -162,6 +175,7 @@
previewMode: 1,
context: 1,
ctid: `fixture-${index}`,
+ containerName: `fixture-container-${index}`,
appendModelPreview: () => {
fallbackCount += 1;
const $fallback = window.jQuery('
Fallback');
@@ -180,10 +194,34 @@
missingWrapper: { ok: missingWrapper.ok, reason: missingWrapper.reason },
invalidSelector: { ok: invalidSelector.ok, reason: invalidSelector.reason },
renderedCount: renderTarget.children().length,
+ renderedContainerId: renderTarget.children().first().attr('data-fv-container-id'),
+ renderedContainerName: renderTarget.children().first().attr('data-fv-container-name'),
fallbackCount
};
}
};
+ window.fixtureDockerLifecyclePendingColors = {
+ run: () => {
+ const $preview = window.jQuery('
')
+ .css('--fvplus-folder-status-started', '#11aa22')
+ .css('--fvplus-folder-status-paused', '#cc9900')
+ .css('--fvplus-folder-status-stopped', '#dd2233');
+ const colors = {};
+ ['start', 'resume', 'restart', 'pause', 'stop'].forEach((action) => {
+ const $surface = window.jQuery('
').attr('data-fv-lifecycle-action', action);
+ const $icon = window.jQuery('
');
+ $surface.append($icon);
+ $preview.append($surface);
+ });
+ window.jQuery(document.body).append($preview);
+ $preview.children().each((_, surface) => {
+ const action = surface.getAttribute('data-fv-lifecycle-action');
+ colors[action] = window.getComputedStyle(surface.querySelector('i')).color;
+ });
+ $preview.remove();
+ return colors;
+ }
+ };
window.localStorage.removeItem(window.FolderViewPlusDockerPreviewActions.PREVIEW_CONTEXT_DIAGNOSTICS_STORAGE_KEY);
const actionsApi = window.FolderViewPlusDockerPreviewActions.createApi({
window,
@@ -277,6 +315,141 @@
};
buildNativeContextPreview('rows-2');
buildNativeContextPreview('unlimited');
+ const childFolderFixtureState = {
+ editCount: 0,
+ actionCount: 0,
+ contextBindings: { default: 0, advanced: 0 },
+ contextClicks: { default: 0, advanced: 0 },
+ contextAudits: 0
+ };
+ const childFolderFixtureFolders = {
+ 'fixture-parent': {
+ name: 'Private parent fixture',
+ settings: { preview: 1, preview_rows: 2, context: 1 },
+ runtimeContainers: {},
+ status: { expanded: false }
+ },
+ 'fixture-child': {
+ name: 'Private child fixture',
+ icon: '',
+ settings: { preview: 1, preview_rows: 2 },
+ runtimeContainers: {},
+ status: { expanded: false }
+ }
+ };
+ const childFolderFixtureHierarchy = {
+ ids: ['fixture-parent', 'fixture-child'],
+ parentById: { 'fixture-parent': '', 'fixture-child': 'fixture-parent' },
+ childrenById: { 'fixture-parent': ['fixture-child'], 'fixture-child': [] }
+ };
+ const layoutChildFolderFixtureRows = ($preview, settings) => {
+ const runtimeShared = window.FolderViewDockerRuntimeShared;
+ const wrappers = runtimeShared.flattenPreviewWrappers($preview);
+ runtimeShared.finalizePreviewRows($preview, wrappers.map((wrapper) => [wrapper]), settings);
+ };
+ const childFolderHierarchyApi = window.FolderViewPlusDockerRuntimeHierarchy.createApi({
+ window,
+ $: window.jQuery,
+ getGlobalFolders: () => childFolderFixtureFolders,
+ getDockerFolderHierarchy: () => childFolderFixtureHierarchy,
+ buildRuntimeContainerMapForFolder: (folderId, includeDescendants) => {
+ if (folderId === 'fixture-child') {
+ return { childMember: { name: 'Private child member', state: true } };
+ }
+ if (folderId === 'fixture-parent' && includeDescendants !== true) {
+ return {
+ parentMemberOne: { name: 'Private parent member one', shortId: 'fixture-parent-one', state: true },
+ parentMemberTwo: { name: 'Private parent member two', shortId: 'fixture-parent-two', state: true }
+ };
+ }
+ return {};
+ },
+ buildDockerPreviewItem: ({ entry }) => {
+ const $item = window.jQuery('
')
+ .attr('data-fv-container-id', entry?.shortId || '')
+ .find('.appname').text(entry?.name || 'Member').end();
+ return { $item, $tooltipTrigger: $item.find('.fv-preview-trigger').first() };
+ },
+ bindDockerNestedPreviewContext: ($item, _tooltipTrigger, _entry, folder) => {
+ const mode = Number(folder?.settings?.context || 1) === 2 ? 'advanced' : 'default';
+ childFolderFixtureState.contextBindings[mode] += 1;
+ $item
+ .attr('data-fixture-parent-context', mode)
+ .off('click.fixtureParentContext')
+ .on('click.fixtureParentContext', (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ childFolderFixtureState.contextClicks[mode] += 1;
+ });
+ return true;
+ },
+ auditDockerPreviewContextBridges: () => {
+ childFolderFixtureState.contextAudits += 1;
+ },
+ applyFolderPreviewLayout: () => {},
+ layoutFolderPreviewRows: layoutChildFolderFixtureRows,
+ editFolder: () => { childFolderFixtureState.editCount += 1; },
+ openFolderActions: () => { childFolderFixtureState.actionCount += 1; },
+ recordChildFolderPreviewRender: actionsApi.recordChildFolderPreviewRender,
+ recordChildFolderPreviewBinding: actionsApi.recordChildFolderPreviewBinding,
+ recordChildFolderPreviewMenuOpen: actionsApi.recordChildFolderPreviewMenuOpen
+ });
+ const renderChildFolderFixture = () => childFolderHierarchyApi.renderNestedAggregatePreview(
+ 'fixture-parent',
+ childFolderFixtureFolders['fixture-parent'],
+ {},
+ { includeChildFolders: true }
+ );
+ renderChildFolderFixture();
+ for (let pass = 0; pass < 2; pass += 1) {
+ layoutChildFolderFixtureRows(
+ window.jQuery('#fixture-child-folder-preview'),
+ childFolderFixtureFolders['fixture-parent'].settings
+ );
+ }
+ window.fixtureChildFolderPreviewContext = {
+ closeMenu() {
+ window.jQuery('.fv-folder-preview-context-menu').remove();
+ window.jQuery(document).off('click.fvFolderPreviewContext keydown.fvFolderPreviewContext');
+ },
+ dispatchTouchClick() {
+ this.closeMenu();
+ const item = document.querySelector('#fixture-child-folder-preview .fv-folder-preview-child');
+ const event = new Event('click', { bubbles: true, cancelable: true });
+ Object.defineProperty(event, 'changedTouches', {
+ value: [{ clientX: 321, clientY: 145 }]
+ });
+ item.dispatchEvent(event);
+ },
+ rerenderMemberContext(contextMode, listViewMode = 'basic') {
+ childFolderFixtureFolders['fixture-parent'].settings.context = Number(contextMode);
+ document.cookie = `docker_listview_mode=${listViewMode}; path=/`;
+ renderChildFolderFixture();
+ },
+ snapshot() {
+ const menu = document.querySelector('.fv-folder-preview-context-menu');
+ return {
+ chipCount: document.querySelectorAll('#fixture-child-folder-preview .fv-folder-preview-child').length,
+ rowCount: document.querySelectorAll('#fixture-child-folder-preview .folder-preview-row').length,
+ menuCount: document.querySelectorAll('.fv-folder-preview-context-menu').length,
+ menuLeft: Number.parseFloat(menu?.style?.left || '0'),
+ menuTop: Number.parseFloat(menu?.style?.top || '0'),
+ menuWidth: Number(menu?.offsetWidth || 180),
+ menuHeight: Number(menu?.offsetHeight || 112),
+ viewportWidth: Number(window.innerWidth || document.documentElement.clientWidth || 0),
+ viewportHeight: Number(window.innerHeight || document.documentElement.clientHeight || 0),
+ menuActions: Array.from(menu?.querySelectorAll('button') || []).map((button) => button.textContent.trim()),
+ focusedAction: document.activeElement?.textContent?.trim() || '',
+ editCount: childFolderFixtureState.editCount,
+ actionCount: childFolderFixtureState.actionCount,
+ memberContextBindings: { ...childFolderFixtureState.contextBindings },
+ memberContextClicks: { ...childFolderFixtureState.contextClicks },
+ memberContextAudits: childFolderFixtureState.contextAudits,
+ listViewMode: document.cookie.includes('docker_listview_mode=advanced') ? 'advanced' : 'basic',
+ diagnostics: actionsApi.getPreviewContextDiagnosticsSnapshot()
+ };
+ }
+ };
window.fixtureNativePreviewContext.breakRows2HandlersAndAudit = () => {
const $preview = window.jQuery('#fixture-context-preview-rows-2');
const rows = $preview.children('.folder-preview-row').get();
@@ -298,7 +471,7 @@
previewInlineHandlerCount: document.querySelectorAll('.fixture-context-preview [onclick], .fixture-context-preview [oncontextmenu]').length,
startedClassCount: document.querySelectorAll('.fixture-context-preview .state.fv-preview-status-started').length,
bridgeCount: document.querySelectorAll('.fixture-context-preview [data-fv-preview-context="native"][role="button"][tabindex="0"]').length,
- previewRowCount: document.querySelectorAll('.fixture-context-preview .folder-preview-row').length,
+ previewRowCount: document.querySelectorAll('#fixture-context-preview-rows-2 .folder-preview-row, #fixture-context-preview-unlimited .folder-preview-row').length,
diagnostics: actionsApi.getPreviewContextDiagnosticsSnapshot()
});
const layoutEngine = window.FolderViewPlusRuntimeColumnLayout.createColumnLayoutEngine({
diff --git a/tests/browser/fixtures/folder-editor.html b/tests/browser/fixtures/folder-editor.html
index c01bcf37..f78a9ead 100644
--- a/tests/browser/fixtures/folder-editor.html
+++ b/tests/browser/fixtures/folder-editor.html
@@ -6,17 +6,44 @@
FolderView Plus folder action fixture
-
+
+
-
+
+
+
diff --git a/tests/browser/fixtures/runtime.fixture.js b/tests/browser/fixtures/runtime.fixture.js
index d3f0ec77..80b9adb5 100644
--- a/tests/browser/fixtures/runtime.fixture.js
+++ b/tests/browser/fixtures/runtime.fixture.js
@@ -12,6 +12,8 @@
let prefs = { pageViewMode: 'folderview', hideEmptyFolders: false, health: { warnStoppedPercent: 60 } };
const events = [];
let refreshCount = 0;
+ let deferHiddenFolderSave = false;
+ let pendingHiddenFolderSave = null;
const hostAdapter = window.FolderViewPlusRuntimeHostAdapters.getOrCreate('docker', { window, document });
const privacyEvents = [];
let privacySwitchInitializeCount = 0;
@@ -128,6 +130,7 @@
const normalizePrefs = (value = {}) => ({
pageViewMode: ['host', 'command'].includes(String(value.pageViewMode || '')) ? value.pageViewMode : 'folderview',
hideEmptyFolders: value.hideEmptyFolders === true,
+ hiddenFolderIds: Array.isArray(value.hiddenFolderIds) ? [...new Set(value.hiddenFolderIds.map(String).filter(Boolean))] : [],
health: { warnStoppedPercent: Number(value.health?.warnStoppedPercent) || 60 }
});
const applyPrefs = (nextPrefs = prefs) => {
@@ -140,7 +143,45 @@
row.hidden = mode === 'command';
});
};
- const api = window.FolderViewPlusDockerRuntimeActionBar.createApi({
+ let api = null;
+ const hiddenFolders = window.FolderViewPlusFoundationModules.dockerHiddenFolders.createApi({
+ window,
+ document,
+ $: window.jQuery,
+ getFolders: () => folders,
+ getPrefs: () => prefs,
+ setPrefs: (value) => { prefs = normalizePrefs(value); },
+ normalizePrefs,
+ normalizeParentId: (value) => String(value || ''),
+ readFolderIdFromRow: (row) => String(row?.dataset?.folderId || ''),
+ readFolderOwnerFromRow: (row) => String(row?.dataset?.folderOwner || ''),
+ runtimeStateStore: { set: () => {} },
+ safeActionRunner: window.FolderViewDockerRuntimeShared.createSafeUiActionRunner(),
+ runGuardedAction: async (_name, action) => {
+ try { return { ok: true, value: await action() }; } catch (error) { return { ok: false, error }; }
+ },
+ savePrefs: async (patch) => {
+ const nextPrefs = normalizePrefs({ ...prefs, ...patch });
+ if (deferHiddenFolderSave) {
+ return new Promise((resolve, reject) => {
+ pendingHiddenFolderSave = { nextPrefs, resolve, reject };
+ });
+ }
+ prefs = nextPrefs;
+ return prefs;
+ },
+ fetchPrefs: async () => prefs,
+ syncDependentUi: () => api?.sync(),
+ getFocusedFolderId: () => '',
+ clearFocusedFolder: () => events.push({ type: 'clear-focus' }),
+ offerUndo: (payload) => api?.offerHiddenFolderUndo(payload),
+ dismissUndo: (folderId = '') => api?.clearHiddenFolderUndo(folderId),
+ translate: (_key, fallback) => fallback,
+ escapeHtml: (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({
+ '&': '&', '<': '<', '>': '>', '"': '"', "'": '''
+ }[char]))
+ });
+ api = window.FolderViewPlusDockerRuntimeActionBar.createApi({
window,
document,
hostAdapter,
@@ -168,6 +209,7 @@
getFocusedFolderId: () => '',
clearFocusedFolder: () => events.push({ type: 'clear-focus' }),
scheduleWidthReflow: () => {},
+ hiddenFolders,
buildFolderHierarchy: () => ({ parentById: { media: '', updates: '', empty: '' } }),
expandFolderBranch: (id) => events.push({ type: 'expand', id }),
collapseFolderBranch: (id) => events.push({ type: 'collapse', id }),
@@ -179,6 +221,28 @@
window.fixtureRuntime = {
api,
+ hiddenFolders,
+ hiddenFolderPersistence: {
+ defer: () => { deferHiddenFolderSave = true; },
+ isPending: () => !!pendingHiddenFolderSave,
+ resolve: () => {
+ const pending = pendingHiddenFolderSave;
+ pendingHiddenFolderSave = null;
+ deferHiddenFolderSave = false;
+ if (!pending) return false;
+ prefs = pending.nextPrefs;
+ pending.resolve(prefs);
+ return true;
+ },
+ reject: () => {
+ const pending = pendingHiddenFolderSave;
+ pendingHiddenFolderSave = null;
+ deferHiddenFolderSave = false;
+ if (!pending) return false;
+ pending.reject(new Error('fixture hidden-folder save failed'));
+ return true;
+ }
+ },
events,
privacyToggle: {
rememberIdentity: () => {
diff --git a/tests/browser/fixtures/runtime.html b/tests/browser/fixtures/runtime.html
index 4a71aef1..f0bcff68 100644
--- a/tests/browser/fixtures/runtime.html
+++ b/tests/browser/fixtures/runtime.html
@@ -24,12 +24,12 @@
| Application | Version | Network | Container IP | Container port | LAN IP:PORT |
- | Media | 1/2 started |
+ | Media | 1/2 started |
| Plex | up-to-date | bridge | 172.17.0.2 | 32400/TCP | 10.0.0.10:32400 |
| Sonarr | up-to-date | bridge | 172.17.0.3 | 8989/TCP | 10.0.0.10:8989 |
- | Updates | 1 update |
+ | Updates | 1 update |
| Toolbox | update ready | bridge | 172.17.0.4 | 8080/TCP | 10.0.0.10:8080 |
- | Empty | 0 members |
+ | Empty | 0 members |
| Orphan | up-to-date | bridge | 172.17.0.8 | 9000/TCP | 10.0.0.10:9000 |
@@ -50,6 +50,7 @@
+
diff --git a/tests/browser/helpers/docker-hidden-folder-context.mjs b/tests/browser/helpers/docker-hidden-folder-context.mjs
new file mode 100644
index 00000000..1a526027
--- /dev/null
+++ b/tests/browser/helpers/docker-hidden-folder-context.mjs
@@ -0,0 +1,31 @@
+import assert from 'node:assert/strict';
+
+export const assertImmediateThemedUndo = async (page, folderId) => {
+ await page.evaluate(() => window.fixtureRuntime.hiddenFolderPersistence.defer());
+ await page.evaluate((id) => { void window.fixtureRuntime.hiddenFolders.hideFolder(id); }, folderId);
+ assert.equal(await page.locator('#docker_list > tr.fv-folder-user-hidden').count(), 3);
+ assert.equal(await page.locator(`#docker_list > tr.folder-id-${folderId}`).isVisible(), false);
+ assert.equal(await page.evaluate(() => window.fixtureRuntime.hiddenFolderPersistence.isPending()), true);
+ const undoButton = page.locator('[data-fvplus-docker-hidden="undo"]');
+ assert.equal(await undoButton.isVisible(), true, 'Undo must render before the deferred preference save resolves');
+ const themedButton = await undoButton.evaluate((button) => {
+ const probe = document.createElement('div');
+ probe.style.cssText = 'position:absolute;left:-9999px;border:1px solid var(--fvplus-ui-border-subtle);background:var(--fvplus-ui-control);color:var(--fvplus-ui-text-primary)';
+ document.body.appendChild(probe);
+ const buttonStyle = getComputedStyle(button);
+ const probeStyle = getComputedStyle(probe);
+ const result = {
+ classApplied: button.classList.contains('fvplus-docker-visibility-button'),
+ borderColor: buttonStyle.borderTopColor, expectedBorderColor: probeStyle.borderTopColor,
+ backgroundColor: buttonStyle.backgroundColor, expectedBackgroundColor: probeStyle.backgroundColor,
+ color: buttonStyle.color, expectedColor: probeStyle.color
+ };
+ probe.remove();
+ return result;
+ });
+ assert.equal(themedButton.classApplied, true);
+ assert.equal(themedButton.borderColor, themedButton.expectedBorderColor);
+ assert.equal(themedButton.backgroundColor, themedButton.expectedBackgroundColor);
+ assert.equal(themedButton.color, themedButton.expectedColor);
+ await page.evaluate(() => window.fixtureRuntime.hiddenFolderPersistence.resolve());
+};
diff --git a/tests/browser/helpers/docker-preview-context.mjs b/tests/browser/helpers/docker-preview-context.mjs
index d13d3fc0..95cfaf34 100644
--- a/tests/browser/helpers/docker-preview-context.mjs
+++ b/tests/browser/helpers/docker-preview-context.mjs
@@ -83,3 +83,81 @@ export const exerciseDockerPreviewContextDiagnostics = async ({ page, baseUrl })
);
assert.equal(result.diagnostics.failureReasons['handler-missing'], 2);
};
+
+export const exerciseChildFolderPreviewContext = async ({ page, baseUrl }) => {
+ await page.goto(`${baseUrl}/docker-layout-stability`, { waitUntil: 'load' });
+ const chip = page.locator('#fixture-child-folder-preview .fv-folder-preview-child');
+ const snapshot = () => page.evaluate(() => window.fixtureChildFolderPreviewContext.snapshot());
+
+ let result = await snapshot();
+ assert.deepEqual([result.chipCount, result.rowCount], [1, 3]);
+ assert.deepEqual(result.memberContextBindings, { default: 2, advanced: 0 });
+ await page.locator('#fixture-child-folder-preview .fv-nested-preview-item').first().click();
+ result = await snapshot();
+ assert.deepEqual(result.memberContextClicks, { default: 1, advanced: 0 });
+
+ await chip.click();
+ result = await snapshot();
+ assert.equal(result.menuCount, 1, 'normal click must open the child-folder menu');
+ assert.ok(result.menuLeft > 0);
+ assert.ok(result.menuTop > 0);
+ assert.deepEqual(result.menuActions, ['Expand to folder', 'Edit folder', 'Open folder actions']);
+ await page.getByRole('menuitem', { name: 'Edit folder' }).click();
+ result = await snapshot();
+ assert.deepEqual([result.menuCount, result.editCount], [0, 1]);
+
+ await chip.focus();
+ await chip.press('Enter');
+ await page.waitForFunction(() => document.activeElement?.textContent?.includes('Expand to folder'));
+ result = await snapshot();
+ assert.equal(result.menuCount, 1, 'keyboard activation must open the child-folder menu');
+ assert.equal(result.focusedAction, 'Expand to folder');
+ assert.ok(result.menuLeft > 0, 'keyboard activation must use the chip geometry');
+ assert.ok(result.menuTop > 0, 'keyboard activation must use the chip geometry');
+ await page.keyboard.press('Escape');
+
+ await chip.click({ button: 'right' });
+ result = await snapshot();
+ assert.equal(result.menuCount, 1, 'right-click must keep opening the child-folder menu');
+ await page.getByRole('menuitem', { name: 'Open folder actions' }).click();
+ result = await snapshot();
+ assert.deepEqual([result.menuCount, result.actionCount], [0, 1]);
+
+ await page.evaluate(() => window.fixtureChildFolderPreviewContext.dispatchTouchClick());
+ result = await snapshot();
+ assert.equal(result.menuCount, 1, 'touch-like activation must open the child-folder menu');
+ const expectedTouchLeft = Math.max(8, Math.min(321, result.viewportWidth - result.menuWidth - 8));
+ const expectedTouchTop = Math.max(8, Math.min(145, result.viewportHeight - result.menuHeight - 8));
+ assert.deepEqual([result.menuLeft, result.menuTop], [expectedTouchLeft, expectedTouchTop]);
+ assert.deepEqual(
+ [result.diagnostics.childFolderPreview.counters.chipsRendered,
+ result.diagnostics.childFolderPreview.counters.bindings,
+ result.diagnostics.childFolderPreview.counters.menuOpenAttempts,
+ result.diagnostics.childFolderPreview.counters.menuOpens,
+ result.diagnostics.childFolderPreview.counters.menuOpenFailures],
+ [1, 1, 4, 4, 0]
+ );
+ assert.deepEqual(
+ result.diagnostics.childFolderPreview.inputMethods,
+ { mouse: 1, keyboard: 1, contextmenu: 1, touch: 1, unknown: 0 }
+ );
+ assert.doesNotMatch(
+ JSON.stringify(result.diagnostics.childFolderPreview),
+ /Private parent fixture|Private child fixture|fixture-parent|fixture-child/
+ );
+
+ await page.evaluate(() => window.fixtureChildFolderPreviewContext.rerenderMemberContext(2, 'advanced'));
+ await page.locator('#fixture-child-folder-preview .fv-nested-preview-item').first().click();
+ result = await snapshot();
+ assert.deepEqual(result.memberContextBindings, { default: 2, advanced: 2 });
+ assert.deepEqual(result.memberContextClicks, { default: 1, advanced: 1 });
+ assert.equal(result.listViewMode, 'advanced');
+
+ await page.evaluate(() => window.fixtureChildFolderPreviewContext.rerenderMemberContext(1, 'advanced'));
+ await page.locator('#fixture-child-folder-preview .fv-nested-preview-item').nth(1).click();
+ result = await snapshot();
+ assert.deepEqual(result.memberContextBindings, { default: 4, advanced: 2 });
+ assert.deepEqual(result.memberContextClicks, { default: 2, advanced: 1 });
+ assert.equal(result.listViewMode, 'advanced');
+ assert.equal(result.memberContextAudits, 3);
+};
diff --git a/tests/dashboard-lifecycle-reconciliation.test.mjs b/tests/dashboard-lifecycle-reconciliation.test.mjs
index 3db18721..52309b95 100644
--- a/tests/dashboard-lifecycle-reconciliation.test.mjs
+++ b/tests/dashboard-lifecycle-reconciliation.test.mjs
@@ -160,6 +160,114 @@ test('Docker page lifecycle defaults resolve container ids and clear expanded pr
assert.equal(finalized.details.remainingBusyPreviewActionIconCount, 0);
});
+test('Docker restart lifecycle keeps its pending surface through the initial running snapshot', async () => {
+ const scheduled = [];
+ const finalized = [];
+ const api = reconcileModule.createApi({
+ window: {
+ setTimeout(handler, delayMs) {
+ scheduled.push({ handler, delayMs });
+ return scheduled.length;
+ }
+ },
+ refreshDockerRuntimeStateInPlace: async () => true,
+ isDockerLifecycleStateSettled: () => true,
+ getDockerLifecycleStateSnapshot: () => ({ state: 'running', active: true, paused: false }),
+ finalizeDockerLifecycleSurface: (request, outcome) => finalized.push({ request, outcome }),
+ lifecycleRefreshDelaysMs: [0, 750]
+ });
+
+ api.runDockerLifecycleRefresh({ action: 'restart', container: 'abc123def456' });
+ scheduled[0].handler();
+ await flushPromises();
+ assert.equal(finalized.length, 0);
+
+ scheduled[1].handler();
+ await flushPromises();
+ assert.equal(finalized.length, 1);
+ assert.equal(finalized[0].outcome.settled, true);
+ assert.equal(finalized[0].outcome.attempt, 2);
+});
+
+test('Docker native lifecycle hook marks and restores a matching compact preview status icon', async () => {
+ const scheduled = [];
+ const iconAttributes = new Map([['class', 'fa fa-play fv-preview-status-started']]);
+ const surfaceAttributes = new Map([
+ ['data-fv-container-id', 'abc123def456'],
+ ['data-fv-container-name', 'example-container']
+ ]);
+ const icon = {
+ getAttribute: (name) => iconAttributes.get(name) || '',
+ setAttribute: (name, value) => iconAttributes.set(name, String(value)),
+ hasAttribute: (name) => iconAttributes.has(name),
+ removeAttribute: (name) => iconAttributes.delete(name)
+ };
+ const surface = {
+ getAttribute: (name) => surfaceAttributes.get(name) || '',
+ setAttribute: (name, value) => surfaceAttributes.set(name, String(value)),
+ removeAttribute: (name) => surfaceAttributes.delete(name),
+ querySelectorAll: () => [icon]
+ };
+ const document = {
+ querySelectorAll(selector) {
+ if (selector.includes('data-fv-container-id')) return [surface];
+ if (selector.includes('data-fv-lifecycle-pending')) {
+ return surfaceAttributes.get('data-fv-lifecycle-pending') === 'true' ? [surface] : [];
+ }
+ if (selector.includes('data-fv-lifecycle-icon-class')) {
+ return iconAttributes.has('data-fv-lifecycle-icon-class') ? [icon] : [];
+ }
+ return [];
+ }
+ };
+ const entry = {
+ shortId: 'abc123def456',
+ name: 'example-container',
+ info: { Id: 'abc123def4567890', Name: 'example-container', State: { Running: true, Paused: false } }
+ };
+ const window = {
+ eventControl: (...args) => {
+ window[args[1]]();
+ return true;
+ },
+ setTimeout(handler, delayMs) {
+ scheduled.push({ handler, delayMs });
+ return scheduled.length;
+ }
+ };
+ const api = reconcileModule.createApi({
+ window,
+ document,
+ getDockerRuntimeInfoEntries: () => [entry],
+ refreshDockerRuntimeStateInPlace: async () => true,
+ syncDockerVisibleFoldersFromRuntimeCache: () => true,
+ lifecycleRefreshDelaysMs: [0],
+ getDockerHostGuardsApi: () => ({
+ wrapHostHook(name, handler) {
+ const original = window[name];
+ window[name] = (...args) => handler({
+ args,
+ invokeOriginal: (...overrideArgs) => original(...overrideArgs)
+ });
+ }
+ })
+ });
+
+ api.bindLifecycleEventControlPatch();
+ window.eventControl({ action: 'restart', container: 'abc123def456' }, 'loadlist');
+ assert.match(iconAttributes.get('class'), /fa-refresh/);
+ assert.match(iconAttributes.get('class'), /fa-spin/);
+ assert.equal(surfaceAttributes.get('aria-busy'), 'true');
+ assert.equal(surfaceAttributes.get('data-fv-lifecycle-action'), 'restart');
+
+ scheduled[0].handler();
+ await flushPromises();
+ assert.equal(iconAttributes.get('class'), 'fa fa-play fv-preview-status-started');
+ assert.equal(surfaceAttributes.has('aria-busy'), false);
+ assert.equal(surfaceAttributes.has('data-fv-lifecycle-action'), false);
+ assert.equal(iconAttributes.has('data-fv-lifecycle-icon-class'), false);
+});
+
test('Dashboard stop followed by start cancels the stale stop tail and converges to running', async () => {
const scheduled = [];
const refreshStates = [false, false, true];
diff --git a/tests/diagnostics-summary-contract.test.mjs b/tests/diagnostics-summary-contract.test.mjs
index b1827244..c595dce7 100644
--- a/tests/diagnostics-summary-contract.test.mjs
+++ b/tests/diagnostics-summary-contract.test.mjs
@@ -60,6 +60,7 @@ const buildIntegrityChecks = (overrides = {}) => ({
invalidAutoRules: { count: 0, rules: [] },
missingManualOrderIds: { count: 0, ids: [] },
missingPinnedFolderIds: { count: 0, ids: [] },
+ missingHiddenFolderIds: { count: 0, ids: [] },
duplicateAssignments: {
explicit: { count: 0, examples: [] },
regex: { count: 0, examples: [] },
@@ -124,3 +125,14 @@ test('diagnostics summary still surfaces concrete path issues first', () => {
assert.ok(dockerCard, 'docker summary card is missing');
assert.equal(dockerCard.detail, 'Missing /boot/config/plugins/folderview.plus/docker.folder.json');
});
+
+test('diagnostics summary identifies stale hidden-folder preferences', () => {
+ const summary = runOverviewSummary({
+ typesData: buildTypesData(buildIntegrityChecks({
+ issuesCount: 1,
+ missingHiddenFolderIds: { count: 1, ids: ['removed-folder'] }
+ }))
+ });
+ const dockerCard = summary.cards.find((card) => card.key === 'docker');
+ assert.equal(dockerCard?.detail, '1 hidden folder id no longer matches an existing folder.');
+});
diff --git a/tests/docker-clone-branch-regression.test.mjs b/tests/docker-clone-branch-regression.test.mjs
index 27d6860a..36218aa4 100644
--- a/tests/docker-clone-branch-regression.test.mjs
+++ b/tests/docker-clone-branch-regression.test.mjs
@@ -76,7 +76,8 @@ test('docker clone payload builder deep-clones mutable folder fields', () => {
icon: 'icon.svg',
settings: {
layout: 'grid',
- nested: { enabled: true }
+ nested: { enabled: true },
+ webui_profiles: [{ id: 'media', name: 'Media tools', containers: ['plex'] }]
},
regex: '^media',
containers: ['plex'],
@@ -98,6 +99,8 @@ test('docker clone payload builder deep-clones mutable folder fields', () => {
assert.equal(payload.parentId, 'child-parent');
assert.notStrictEqual(payload.settings, source.settings);
assert.notStrictEqual(payload.settings.nested, source.settings.nested);
+ assert.notStrictEqual(payload.settings.webui_profiles, source.settings.webui_profiles);
+ assert.deepEqual(payload.settings.webui_profiles, source.settings.webui_profiles);
assert.notStrictEqual(payload.containers, source.containers);
assert.notStrictEqual(payload.actions, source.actions);
assert.notStrictEqual(payload.actions[0], source.actions[0]);
diff --git a/tests/docker-folder-context-feature-actions.test.mjs b/tests/docker-folder-context-feature-actions.test.mjs
index 1d45ef98..9920e651 100644
--- a/tests/docker-folder-context-feature-actions.test.mjs
+++ b/tests/docker-folder-context-feature-actions.test.mjs
@@ -19,17 +19,23 @@ const runtimeSharedControlsScript = fs.readFileSync(
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-controls.js'),
'utf8'
);
+const webuiProfilesScript = fs.readFileSync(
+ path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js'),
+ 'utf8'
+);
const runtimeSharedControls = require(path.join(
repoRoot,
'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.shared-controls.js'
));
+const dockerRuntimeActions = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js');
+const webuiProfiles = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js');
test('docker folder context supports open-all-webui actions with scoped options', () => {
assert.match(dockerScript, /const collectFolderWebuiTargets = \(id, includeDescendants = true, runningOnly = true\) => \{/);
assert.match(dockerRuntimeActionsScript, /const collectFolderWebuiTargets = \(id, includeDescendants = true, runningOnly = true\) =>/);
assert.match(runtimeSharedControlsScript, /const openWebuiPopupWindow = \(url, targetName = '_blank'\) =>/);
- assert.match(dockerScript, /const openFolderWebuisFromMenu = \(id, runningOnly = true, includeDescendants = false\) => \{/);
- assert.match(dockerRuntimeActionsScript, /const openFolderWebuisFromMenu = \(id, runningOnly = true, includeDescendants = false\) =>/);
+ assert.match(dockerScript, /const openFolderWebuisFromMenu = \(id, runningOnly = true, includeDescendants = false, profileId = ''\) =>/);
+ assert.match(dockerRuntimeActionsScript, /const openFolderWebuisFromMenu = \(id, runningOnly = true, includeDescendants = false, profileId = ''\) =>/);
assert.match(dockerScript, /Open all WebUIs/);
assert.match(dockerScript, /collectFolderWebuiTargets\(id, false, true\)/);
assert.match(dockerRuntimeActionsScript, /entry\?\.state === true && entry\?\.pause !== true/);
@@ -40,6 +46,42 @@ test('docker folder context supports open-all-webui actions with scoped options'
assert.match(dockerRuntimeActionsScript, /Blocked WebUIs \(manual open\)/);
assert.match(dockerScript, /dockerRuntimeInfoByName/);
assert.match(dockerScript, /openFolderWebuisFromMenu\(id, true, false\)/);
+ assert.match(dockerScript, /appendRuntimeMenuItems/);
+ assert.match(webuiProfilesScript, /docker\.webui-profiles\.open/);
+ assert.match(dockerScript, /editFolder\(id, \{ section: 'webuiProfiles' \}\)/);
+});
+
+test('custom WebUI profile launches only selected running direct members', async () => {
+ const opened = [];
+ const api = dockerRuntimeActions.createApi({
+ window: { setTimeout, location: { host: 'tower.local' } },
+ document: {},
+ webuiProfiles,
+ getGlobalFolders: () => ({
+ media: {
+ settings: {
+ webui_profiles: [{ id: 'media-tools', name: 'Media tools', containers: ['Plex', 'Tautulli', 'Stopped'] }]
+ }
+ }
+ }),
+ getScopedRuntimeContainersForFolder: () => ({
+ Plex: { state: true, pause: false, webui: 'http://plex/' },
+ Tautulli: { state: true, pause: false, webui: 'http://tautulli/' },
+ Stopped: { state: false, pause: false, webui: 'http://stopped/' },
+ Other: { state: true, pause: false, webui: 'http://other/' }
+ }),
+ getSafeWebuiUrl: (value) => value,
+ openWebuiPopupWindow: (url) => { opened.push(url); return true; },
+ runDockerGuardedAction: async (_name, action) => action()
+ });
+
+ api.openFolderWebuisFromMenu('media', true, false, 'media-tools');
+ await new Promise((resolve) => setImmediate(resolve));
+ assert.deepEqual(opened, ['http://plex/', 'http://tautulli/']);
+
+ api.openFolderWebuisFromMenu('media', true, false, 'missing-profile');
+ await new Promise((resolve) => setImmediate(resolve));
+ assert.deepEqual(opened, ['http://plex/', 'http://tautulli/']);
});
test('open-all WebUI popup detection uses a secured blank handle before navigation', () => {
diff --git a/tests/docker-folder-row-quick-actions.test.mjs b/tests/docker-folder-row-quick-actions.test.mjs
index da48bcd5..8c969043 100644
--- a/tests/docker-folder-row-quick-actions.test.mjs
+++ b/tests/docker-folder-row-quick-actions.test.mjs
@@ -8,6 +8,10 @@ const dockerScript = fs.readFileSync(
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js'),
'utf8'
);
+const dockerHiddenFoldersScript = fs.readFileSync(
+ path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hidden-folders.js'),
+ 'utf8'
+);
const dockerPreviewActionsScript = fs.readFileSync(
path.join(repoRoot, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.preview-actions.js'),
'utf8'
@@ -46,13 +50,18 @@ const dockerFolderSameLevelMoveBlock = extractConstFunctionBlock(
'ensureDockerFolderUnlocked'
);
-test('docker context menu keeps focus/pin/lock quick actions at the top', () => {
+test('docker context menu keeps focus/pin/lock/hide quick actions at the top', () => {
assert.match(dockerScript, /text:\s*focused[\s\S]*getDockerMenuLabel\('clear-focus-folder',\s*'Clear focus'\)/);
assert.match(dockerScript, /text:\s*pinned[\s\S]*getDockerMenuLabel\('unpin-folder',\s*'Unpin folder'\)/);
assert.match(dockerScript, /text:\s*locked[\s\S]*getDockerMenuLabel\('unlock-folder',\s*'Unlock folder'\)/);
assert.match(dockerScript, /toggleDockerFolderFocus\(id\)/);
assert.match(dockerScript, /toggleDockerFolderPin\(id\)/);
assert.match(dockerScript, /toggleDockerFolderLock\(id\)/);
+ assert.match(dockerHiddenFoldersScript, /translate\('docker\.folder\.hide',\s*'Hide folder'\)/);
+ assert.match(dockerHiddenFoldersScript, /hiddenOwnerId \? restoreFolder\(hiddenOwnerId\) : hideFolder\(id\)/);
+ assert.match(dockerScript, /minimumItems:\s*4/);
+ assert.match(dockerScript, /maximumItems:\s*4/);
+ assert.match(dockerHiddenFoldersScript, /svgIcon\(\$icon\.hasClass\('fa-eye-slash'\)/);
assert.match(dockerScript, /queueDockerFolderContextQuickIcons\(/);
assert.match(dockerScript, /createDockerContextMenuQuickStripAdapter/);
assert.match(dockerScript, /dockerContextQuickStripAdapter/);
@@ -248,6 +257,20 @@ test('docker lifecycle reconciliation restores expanded preview action icons fro
assert.match(dockerScript, /getDockerRuntimeInfoEntries: \(\) => Object\.values\(dockerRuntimeInfoByName \|\| \{\}\),/);
assert.match(dockerScript, /syncDockerVisibleFoldersFromRuntimeCache: \(changedNames = null\) => syncDockerVisibleFoldersFromRuntimeCache\(changedNames\),/);
assert.match(dockerReconcileScript, /remainingBusyPreviewActionIconCount:/);
+ assert.match(dockerScript, /\.attr\('data-fv-container-id', String\(ctid \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerScript, /\.attr\('data-fv-container-name', String\(previewEntry\?\.name \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerReconcileScript, /const markDockerLifecycleSurfacePending = \(request = \{\}\) =>/);
+ assert.match(dockerReconcileScript, /const clearDockerLifecycleSurfacePending = \(request = \{\}\) =>/);
+ assert.match(dockerReconcileScript, /const restartWarmupPending = action === 'restart'/);
+ assert.match(dockerPreviewActionsScript, /\.attr\('data-fv-container-id', String\(options\.ctid \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerPreviewActionsScript, /\.attr\('data-fv-container-name', String\(options\.containerName \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerScript, /context: folder\.settings\.context, ctid, containerName: previewEntry\?\.name, autostart/);
+ assert.match(dockerScript, /span\.hand:last`\)[\s\S]*?\.attr\('data-fv-container-id', String\(ctid \|\| ''\)\.trim\(\)\)[\s\S]*?\.attr\('data-fv-container-name', String\(previewEntry\?\.name \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerScript, /lstSpan\.children\('span\.inner:last'\)[\s\S]*?\.attr\('data-fv-container-id', String\(ctid \|\| ''\)\.trim\(\)\)[\s\S]*?\.attr\('data-fv-container-name', String\(previewEntry\?\.name \|\| ''\)\.trim\(\)\)/);
+ assert.match(dockerCss, /\.folder-preview i\.fv-preview-lifecycle-pending\s*\{[^}]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
+ assert.match(dockerCss, /data-fv-lifecycle-action="restart"[^}]*fv-preview-lifecycle-pending[\s\S]*?var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
+ assert.match(dockerCss, /data-fv-lifecycle-action="pause"[^}]*fv-preview-lifecycle-pending\s*\{[^}]*var\(--fvplus-folder-status-paused,\s*var\(--fvplus-status-paused\)\)/);
+ assert.match(dockerCss, /data-fv-lifecycle-action="stop"[^}]*fv-preview-lifecycle-pending\s*\{[^}]*var\(--fvplus-folder-status-stopped,\s*var\(--fvplus-status-stopped\)\)/);
});
test('docker incremental lifecycle sync refreshes initialized preview menus from canonical runtime state', () => {
diff --git a/tests/docker-hidden-folder-visibility.test.mjs b/tests/docker-hidden-folder-visibility.test.mjs
new file mode 100644
index 00000000..71b51960
--- /dev/null
+++ b/tests/docker-hidden-folder-visibility.test.mjs
@@ -0,0 +1,136 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import path from 'node:path';
+import { createRequire } from 'node:module';
+
+const root = path.resolve(process.cwd());
+const read = (relativePath) => fs.readFileSync(path.join(root, relativePath), 'utf8');
+const dockerJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js');
+const hiddenFoldersJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hidden-folders.js');
+const require = createRequire(import.meta.url);
+const hiddenFoldersModule = require(path.join(root, 'src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hidden-folders.js'));
+const prefsJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils-prefs.js');
+const libPrefsPhp = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.prefs.php');
+const validationPhp = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.validation.php');
+const diagnosticsPhp = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.diagnostics-support-bundle.php');
+const pageDiagnosticsJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.page-diagnostics.js');
+
+test('hidden folder ids use normalized conflict-safe server preferences', () => {
+ assert.match(libPrefsPhp, /'hiddenFolderIds'\s*=>\s*\[\]/);
+ assert.match(libPrefsPhp, /\$normalized\['hiddenFolderIds'\]\s*=\s*normalizeStringIdList/);
+ assert.match(validationPhp, /fvplus_validation_assert_list_of_scalarish\(\$payload\['hiddenFolderIds'\], 'hiddenFolderIds'/);
+ assert.match(prefsJs, /const hiddenFolderIds = normalizeStringIdList\(incoming\.hiddenFolderIds\)/);
+ assert.match(hiddenFoldersJs, /savePrefs\(\{ hiddenFolderIds \}/);
+ assert.match(hiddenFoldersJs, /safeActionRunner\.run\('docker-hidden-folders'/);
+ assert.match(hiddenFoldersJs, /queueIfBusy:\s*true/);
+ assert.match(hiddenFoldersJs, /if \(!intent\.isLatest\(\)\) return/);
+ assert.match(hiddenFoldersJs, /pendingIdsOverride = \{ ids: normalizeIds\(nextIds\), expiresAt:/);
+ assert.match(dockerJs, /dockerHiddenFoldersApi\.reconcilePrefs\(applyDockerPinnedFolderPrefsOverride/);
+});
+
+test('hidden parent folders suppress their complete visual branch without changing membership', () => {
+ assert.match(hiddenFoldersJs, /cursor = normalizeParentId\(folder\?\.parentId \|\| folder\?\.parent_id \|\| ''\)/);
+ assert.match(hiddenFoldersJs, /const ownerId = folderId \|\| readFolderOwnerFromRow\(row\)/);
+ assert.match(hiddenFoldersJs, /\$row\.toggleClass\('fv-folder-user-hidden', hidden\)/);
+ assert.match(hiddenFoldersJs, /if \(hidden && getOwnerId\(getFocusedFolderId\(\)\)\) clearFocusedFolder\(\)/);
+});
+
+test('hidden-folder hierarchy resolves explicit ancestors without exposing identities', () => {
+ const prefs = { hiddenFolderIds: ['root', 'root', ''] };
+ const api = hiddenFoldersModule.createApi({
+ getPrefs: () => prefs,
+ getFolders: () => ({ root: {}, child: { parentId: 'root' }, other: {} }),
+ normalizeParentId: (value) => String(value || '')
+ });
+ assert.deepEqual(api.normalizeIds(prefs.hiddenFolderIds), ['root']);
+ assert.equal(api.getOwnerId('child'), 'root');
+ assert.equal(api.getOwnerId('other'), '');
+ assert.deepEqual(api.getSummary().effectiveIds.sort(), ['child', 'root']);
+});
+
+test('newer optimistic hidden-folder intent survives an older preference broadcast', () => {
+ let prefs = { hiddenFolderIds: [] };
+ const emptyCollection = { toggleClass: () => emptyCollection, each: () => emptyCollection };
+ const api = hiddenFoldersModule.createApi({
+ $: () => emptyCollection,
+ getPrefs: () => prefs,
+ setPrefs: (value) => { prefs = value; },
+ normalizePrefs: (value) => ({ ...value, hiddenFolderIds: Array.isArray(value.hiddenFolderIds) ? value.hiddenFolderIds : [] }),
+ getFolders: () => ({ root: {} }),
+ runtimeStateStore: { set: () => {} },
+ safeActionRunner: {
+ run: (_key, _action, settings) => {
+ settings.onIntent({ isLatest: () => true });
+ return new Promise(() => {});
+ }
+ }
+ });
+ void api.hideFolder('root');
+ assert.deepEqual(api.reconcilePrefs({ hiddenFolderIds: [] }).hiddenFolderIds, ['root']);
+});
+
+test('hide offers Undo before deferred persistence resolves', async () => {
+ let prefs = { hiddenFolderIds: [] };
+ let resolveSave;
+ const undoOffers = [];
+ const emptyCollection = { toggleClass: () => emptyCollection, each: () => emptyCollection };
+ const api = hiddenFoldersModule.createApi({
+ $: () => emptyCollection,
+ getPrefs: () => prefs,
+ setPrefs: (value) => { prefs = value; },
+ normalizePrefs: (value) => ({ ...value, hiddenFolderIds: Array.isArray(value.hiddenFolderIds) ? value.hiddenFolderIds : [] }),
+ getFolders: () => ({ root: { name: 'Root' } }),
+ runtimeStateStore: { set: () => {} },
+ safeActionRunner: {
+ run: async (_key, action, settings) => {
+ const intent = { isLatest: () => true };
+ settings.onIntent(intent);
+ return action(intent);
+ }
+ },
+ savePrefs: () => new Promise((resolve) => { resolveSave = resolve; }),
+ offerUndo: (payload) => undoOffers.push(payload)
+ });
+ const pending = api.hideFolder('root');
+ assert.deepEqual(prefs.hiddenFolderIds, ['root']);
+ assert.deepEqual(undoOffers, [{ folderId: 'root', folderName: 'Root' }]);
+ resolveSave({ hiddenFolderIds: ['root'] });
+ assert.equal(await pending, true);
+ assert.equal(undoOffers.length, 1);
+});
+
+test('failed hide dismisses only its optimistic Undo notice', async () => {
+ let prefs = { hiddenFolderIds: [] };
+ const dismissed = [];
+ const emptyCollection = { toggleClass: () => emptyCollection, each: () => emptyCollection };
+ const api = hiddenFoldersModule.createApi({
+ $: () => emptyCollection,
+ getPrefs: () => prefs,
+ setPrefs: (value) => { prefs = value; },
+ normalizePrefs: (value) => ({ ...value, hiddenFolderIds: Array.isArray(value.hiddenFolderIds) ? value.hiddenFolderIds : [] }),
+ getFolders: () => ({ root: { name: 'Root' } }),
+ runtimeStateStore: { set: () => {} },
+ safeActionRunner: {
+ run: async (_key, action, settings) => {
+ const intent = { isLatest: () => true };
+ settings.onIntent(intent);
+ return action(intent);
+ }
+ },
+ savePrefs: async () => { throw new Error('save failed'); },
+ fetchPrefs: async () => ({ hiddenFolderIds: [] }),
+ dismissUndo: (folderId) => dismissed.push(folderId)
+ });
+ assert.equal(await api.hideFolder('root'), false);
+ assert.deepEqual(prefs.hiddenFolderIds, []);
+ assert.deepEqual(dismissed, ['root']);
+});
+
+test('hidden-folder diagnostics retain counts and hash folder identities', () => {
+ assert.match(diagnosticsPhp, /prefs\.hiddenFolders\.\*/);
+ assert.match(diagnosticsPhp, /diagnosticsSupportBundleRedactFolderIdList/);
+ assert.match(pageDiagnosticsJs, /hiddenFolderSelections/);
+ assert.match(pageDiagnosticsJs, /effectivelyHiddenFolders/);
+ assert.match(pageDiagnosticsJs, /revealHiddenFolders/);
+});
diff --git a/tests/docker-preview-overflow-feature.test.mjs b/tests/docker-preview-overflow-feature.test.mjs
index 9f85df68..a423f073 100644
--- a/tests/docker-preview-overflow-feature.test.mjs
+++ b/tests/docker-preview-overflow-feature.test.mjs
@@ -13,6 +13,7 @@ const folderPreviewModelJs = read('src/folderview.plus/usr/local/emhttp/plugins/
const folderEditorSharedJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.shared.js');
const dockerJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js');
const dockerPreviewActionsScript = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.preview-actions.js');
+const dockerChildFolderPreviewMenuJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js');
const sharedRuntimeJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.shared.js');
const dockerRuntimeHierarchyJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js');
const dockerCss = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/styles/docker.css');
@@ -156,20 +157,36 @@ test('docker runtime applies preview row layout limits and keeps compact preview
assert.match(folderPreviewModelJs, /const formatRuntimeCountLabel = \(startedCount, memberCount\) => \{/);
assert.match(dockerRuntimeHierarchyJs, /previewModelModule\.createChildFolderPreviewModel\(\{/);
assert.match(dockerRuntimeHierarchyJs, /runtimeCountLabel/);
- assert.match(dockerRuntimeHierarchyJs, /const showFolderPreviewContextMenu = \(event, rootId, childId, childName\) => \{/);
- assert.match(dockerRuntimeHierarchyJs, /addAction\('Expand to folder', 'fa-level-down'/);
- assert.match(dockerRuntimeHierarchyJs, /addAction\('Edit folder', 'fa-pencil'/);
- assert.match(dockerRuntimeHierarchyJs, /addAction\('Open folder actions', 'fa-bars'/);
- assert.match(dockerRuntimeHierarchyJs, /addAction\('Open folder actions', 'fa-bars', \(\) => \{[\s\S]*expandFolderPathToChild\(rootId, childId\);[\s\S]*scrollFolderRowIntoView\(childId\);[\s\S]*openFolderActions\(childId\);/);
+ assert.match(dockerChildFolderPreviewMenuJs, /const resolveInputMethod = \(event = null\) => \{/);
+ assert.match(dockerChildFolderPreviewMenuJs, /const resolveActivationPoint = \(event = null, \$item = null\) => \{/);
+ assert.match(dockerChildFolderPreviewMenuJs, /const show = \(options = \{\}\) => \{/);
+ assert.match(dockerChildFolderPreviewMenuJs, /addAction\('Expand to folder', 'fa-level-down'/);
+ assert.match(dockerChildFolderPreviewMenuJs, /addAction\('Edit folder', 'fa-pencil'/);
+ assert.match(dockerChildFolderPreviewMenuJs, /addAction\('Open folder actions', 'fa-bars'/);
+ assert.match(dockerChildFolderPreviewMenuJs, /addAction\('Open folder actions', 'fa-bars', \(\) => \{[\s\S]*expandFolderPathToChild\(rootId, childId\);[\s\S]*scrollFolderRowIntoView\(childId\);[\s\S]*openFolderActions\(childId\);/);
assert.match(dockerRuntimeHierarchyJs, /data-folder-preview-root/);
assert.match(dockerRuntimeHierarchyJs, /data-folder-preview-child/);
assert.match(dockerRuntimeHierarchyJs, /data-folder-preview-depth/);
+ assert.match(dockerRuntimeHierarchyJs, /aria-haspopup="menu"/);
+ assert.match(dockerRuntimeHierarchyJs, /childFolderPreviewMenuApi\?\.show\?\.\(\{/);
+ assert.match(dockerRuntimeHierarchyJs, /click\.fvChildFolderPreviewMenu keydown\.fvChildFolderPreviewMenu/);
+ assert.match(dockerRuntimeHierarchyJs, /contextmenu\.fvChildFolderPreviewMenu/);
+ assert.match(dockerRuntimeHierarchyJs, /recordChildFolderPreviewRender\(\);/);
+ assert.match(dockerRuntimeHierarchyJs, /recordChildFolderPreviewBinding\(\);/);
+ assert.doesNotMatch(dockerRuntimeHierarchyJs, /\$item\.on\('click keydown',[\s\S]*expandFolderPathToChild\(parentId, childId\);/);
assert.match(dockerRuntimeHierarchyJs, /includeChildFolders\s*:\s*shouldHideNestedPreviewItems\(folder\?\.settings \|\| \{\}\)/);
assert.match(dockerRuntimeHierarchyJs, /buildRuntimeContainerMapForFolder\(id, false\)/);
+ assert.match(dockerRuntimeHierarchyJs, /bindDockerNestedPreviewContext\(item, \$tooltipTrigger, entry, folder, id\);/);
+ assert.match(dockerRuntimeHierarchyJs, /auditDockerPreviewContextBridges\(\$preview, folder\?\.settings \|\| \{\}\);/);
+ assert.match(dockerJs, /const bindDockerNestedPreviewContext = \(\$item, \$tooltipTrigger, entry = \{\}, folder = \{\}, folderId = ''\) =>/);
+ assert.match(dockerPreviewActionsScript, /const findDockerNestedPreviewSourceRow = \(entry = \{\}\) =>/);
+ assert.match(dockerPreviewActionsScript, /const bindDockerNestedPreviewContext = \(options = \{\}\) =>/);
+ assert.match(dockerPreviewActionsScript, /findDockerNestedPreviewSourceRow\(options\.entry \|\| \{\}\)/);
+ assert.match(dockerJs, /dockerAdvancedPreviewContextBindersByName\.get\(containerName\)/);
+ assert.match(dockerJs, /bindAdvancedContext\(\$trigger, \{ folder, folderId \}\)/);
assert.match(dockerRuntimeHierarchyJs, /const depthLimit = normalizeChildFolderPreviewDepth\(folder\?\.settings \|\| \{\}\);/);
assert.match(dockerRuntimeHierarchyJs, /for \(const descendant of getFolderPreviewDescendants\(id, depthLimit\)\)/);
assert.doesNotMatch(dockerRuntimeHierarchyJs, /for \(const childId of getFolderChildren\(id\)\) \{[\s\S]*buildChildFolderPreviewItem\(id, childId, childFolder\)/);
- assert.match(dockerRuntimeHierarchyJs, /expandFolderPathToChild\(parentId, childId\);/);
assert.match(dockerJs, /editFolder: \(id\) => editFolder\(id\)/);
assert.match(dockerJs, /openFolderActions: \(id\) => \{/);
assert.match(dockerJs, /const trigger = document\.getElementById\(String\(id \|\| ''\)\.trim\(\)\);/);
@@ -178,6 +195,9 @@ test('docker runtime applies preview row layout limits and keeps compact preview
assert.match(dockerJs, /const clientY = rect \? rect\.top \+ Math\.max\(1, rect\.height \/ 2\) : 0;/);
assert.match(dockerJs, /trigger\.dispatchEvent\(new MouseEvent\('click'/);
assert.match(dockerJs, /clientX,\s*[\r\n]+\s*clientY/);
+ assert.match(dockerJs, /recordChildFolderPreviewMenuOpen: \(details\) => getDockerPreviewActionsApi\(\)\?\.recordChildFolderPreviewMenuOpen\?\.\(details\)/);
+ assert.match(dockerPreviewActionsScript, /childFolderPreview:\s*\{/);
+ assert.match(dockerPreviewActionsScript, /recordChildFolderPreviewMenuOpen\(details = \{\}\) \{/);
assert.match(dockerJs, /addDockerFolderContext\(id\);/);
assert.match(dockerJs, /const maxItemsPerRow = Math\.max\(1,\s*getFolderPreviewItemsPerRow\(settings\)\)/);
assert.match(dockerJs, /const \$measurement = availableWidth > 0/);
diff --git a/tests/docker-runtime-action-bar.test.mjs b/tests/docker-runtime-action-bar.test.mjs
index 2ababeb7..ff9a8b16 100644
--- a/tests/docker-runtime-action-bar.test.mjs
+++ b/tests/docker-runtime-action-bar.test.mjs
@@ -8,6 +8,7 @@ const repoRoot = path.resolve(process.cwd());
const read = (relativePath) => fs.readFileSync(path.join(repoRoot, relativePath), 'utf8');
const dockerJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.js');
const actionBarJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.action-bar.js');
+const hiddenFoldersJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hidden-folders.js');
const require = createRequire(import.meta.url);
const actionBarModule = require(path.join(
repoRoot,
@@ -38,7 +39,9 @@ test('FolderView action bar exposes the complete folder and troubleshooting cont
'Health Issues',
'Manage Folders',
'View',
- 'Tools'
+ 'Tools',
+ 'Hidden folders',
+ 'Restore all hidden folders'
]) {
assert.ok(
actionBarJs.includes(`'${label}'`) || actionBarJs.includes(`>${label}`),
@@ -211,3 +214,29 @@ test('hiding empty folders reconciles an active empty-only filter before renderi
assert.match(actionBarJs, /if \(reconcileFilterWithPrefs\(\)\) applyFilterState\(\)/);
assert.match(actionBarJs, /actionMenuOpen = '';[\s\S]{0,1200}if \(tool === 'reset'\) resetView\(\)/);
});
+
+test('hidden folder recovery remains available outside the hidden runtime rows', () => {
+ assert.match(actionBarJs, /data-fvplus-docker-hidden="toggle-reveal"/);
+ assert.match(actionBarJs, /data-fvplus-docker-hidden="restore-all"/);
+ assert.match(actionBarJs, /All folder rows are hidden\./);
+ assert.match(actionBarJs, /data-fvplus-docker-hidden="undo"/);
+ assert.match(actionBarJs, /fvplus-docker-visibility-button/);
+ assert.match(actionBarJs, /const clearHiddenFolderUndo = \(folderId = ''\) =>/);
+ assert.match(actionBarJs, /setRevealHiddenFolders\(false\)/);
+ assert.doesNotMatch(actionBarJs, /resetView[\s\S]{0,500}restoreAllHiddenFolders/);
+ assert.match(dockerCss, /#docker_list > tr\.fv-folder-user-hidden\s*\{[\s\S]*display:\s*none !important/);
+ assert.match(dockerCss, /body\.fvplus-docker-reveal-hidden #docker_list > tr\.fv-folder-user-hidden/);
+ assert.match(dockerCss, /\.fvplus-docker-visibility-notice > \.fvplus-docker-visibility-button\s*\{[\s\S]*--fvplus-ui-border-subtle[\s\S]*--fvplus-ui-control[\s\S]*--fvplus-ui-text-primary/);
+ assert.match(hiddenFoldersJs, /const restoreAll = async \(\) =>/);
+ assert.match(hiddenFoldersJs, /hiddenFolderIds/);
+});
+
+test('hidden-folder Undo dismissal is scoped to the matching folder', () => {
+ const api = actionBarModule.createApi({
+ window: { addEventListener() {}, setTimeout: () => 1, clearTimeout() {}, Element: null, HTMLElement: null },
+ document: { addEventListener() {}, querySelectorAll: () => [], getElementById: () => null, querySelector: () => null }
+ });
+ api.offerHiddenFolderUndo({ folderId: 'media', folderName: 'Media' });
+ assert.equal(api.clearHiddenFolderUndo('updates'), false);
+ assert.equal(api.clearHiddenFolderUndo('media'), true);
+});
diff --git a/tests/docker-runtime-shared-architecture.test.mjs b/tests/docker-runtime-shared-architecture.test.mjs
index 1edc6fdd..bbab2e95 100644
--- a/tests/docker-runtime-shared-architecture.test.mjs
+++ b/tests/docker-runtime-shared-architecture.test.mjs
@@ -17,6 +17,7 @@ const runtimeSharedDiagnosticsJs = read('src/folderview.plus/usr/local/emhttp/pl
const dockerModulesJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.modules.js');
const dockerRuntimeInfoJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.info.js');
const dockerPreviewActionsJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.preview-actions.js');
+const dockerChildFolderPreviewMenuJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js');
const dockerRuntimeHierarchyJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js');
const dockerColumnControllerJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.column-controller.js');
const dockerRuntimeActionsJs = read('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.actions.js');
@@ -54,6 +55,7 @@ test('docker runtime page loads shared runtime module before docker modules/runt
const modulesIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.modules.js');
const runtimeInfoIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.info.js');
const previewActionsIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.preview-actions.js');
+ const childFolderPreviewMenuIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.child-folder-preview-menu.js');
const runtimeHierarchyIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.hierarchy.js');
const runtimeActionsIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.actions.js');
const hostGuardsIndex = dockerPage.indexOf('/plugins/folderview.plus/scripts/docker.runtime.host-guards.js');
@@ -78,6 +80,7 @@ test('docker runtime page loads shared runtime module before docker modules/runt
assert.ok(modulesIndex >= 0, 'docker modules script include is missing');
assert.ok(runtimeInfoIndex >= 0, 'docker runtime info script include is missing');
assert.ok(previewActionsIndex >= 0, 'docker preview actions script include is missing');
+ assert.ok(childFolderPreviewMenuIndex >= 0, 'child-folder preview menu script include is missing');
assert.ok(runtimeHierarchyIndex >= 0, 'docker hierarchy script include is missing');
assert.ok(runtimeActionsIndex >= 0, 'docker actions script include is missing');
assert.ok(hostGuardsIndex >= 0, 'docker host guards script include is missing');
@@ -106,7 +109,8 @@ test('docker runtime page loads shared runtime module before docker modules/runt
assert.ok(hostAdapterIndex < orderingIndex && orderingIndex < runtimeIndex, 'shared ordering must load after the host adapter and before docker.js');
assert.ok(modulesIndex < runtimeInfoIndex, 'docker.modules.js must load before docker.runtime.info.js');
assert.ok(runtimeInfoIndex < previewActionsIndex, 'docker.runtime.info.js must load before docker.runtime.preview-actions.js');
- assert.ok(previewActionsIndex < runtimeHierarchyIndex, 'docker preview action helpers must load before docker.runtime.hierarchy.js');
+ assert.ok(previewActionsIndex < childFolderPreviewMenuIndex, 'docker preview action helpers must load before child-folder preview menus');
+ assert.ok(childFolderPreviewMenuIndex < runtimeHierarchyIndex, 'child-folder preview menus must load before docker.runtime.hierarchy.js');
assert.ok(runtimeHierarchyIndex < runtimeActionsIndex, 'docker hierarchy helpers must load before docker.runtime.actions.js');
assert.ok(runtimeActionsIndex < hostGuardsIndex, 'docker action helpers must load before docker.runtime.host-guards.js');
assert.ok(hostGuardsIndex < diagnosticsIndex, 'docker host guards must load before docker.runtime.diagnostics.js');
@@ -155,6 +159,10 @@ test('docker extracted helper modules export createApi entry points with safe gl
assert.match(dockerPreviewActionsJs, /root\.FolderViewPlusDockerPreviewActions = factory\(\);/);
assert.match(dockerPreviewActionsJs, /root\.FolderViewPlusDockerPreviewActionsModuleLoaded = true;/);
assert.match(dockerPreviewActionsJs, /const createApi = \(deps = \{\}\) =>/);
+ assert.match(dockerChildFolderPreviewMenuJs, /^\/\/ @ts-check/m);
+ assert.match(dockerChildFolderPreviewMenuJs, /const fallbackWindow = typeof globalThis !== 'undefined'/);
+ assert.match(dockerChildFolderPreviewMenuJs, /root\.FolderViewPlusFoundationModules\.dockerChildFolderPreviewMenu = factory\(\);/);
+ assert.match(dockerChildFolderPreviewMenuJs, /const createApi = \(deps = \{\}\) =>/);
assert.match(dockerRuntimeHierarchyJs, /^\/\/ @ts-check/m);
assert.match(dockerRuntimeHierarchyJs, /const fallbackWindow = typeof globalThis !== 'undefined'/);
assert.match(dockerRuntimeHierarchyJs, /root\.FolderViewPlusDockerRuntimeHierarchy = factory\(\);/);
@@ -213,6 +221,7 @@ test('docker runtime consumes shared state store and guarded async action wrappe
assert.match(dockerJs, /const dockerRuntimeShared = window\.FolderViewDockerRuntimeShared \|\| \{\};/);
assert.match(dockerJs, /const dockerRuntimeInfoModule = window\.FolderViewPlusDockerRuntimeInfo \|\| null;/);
assert.match(dockerJs, /const dockerPreviewActionsModule = window\.FolderViewPlusDockerPreviewActions \|\| null;/);
+ assert.match(dockerJs, /const dockerChildFolderPreviewMenuModule = window\.FolderViewPlusFoundationModules\?\.dockerChildFolderPreviewMenu \|\| null;/);
assert.match(dockerJs, /const dockerRuntimeHierarchyModule = window\.FolderViewPlusDockerRuntimeHierarchy \|\| null;/);
assert.match(dockerJs, /const dockerRuntimeActionsModule = window\.FolderViewPlusDockerRuntimeActions \|\| null;/);
assert.match(dockerJs, /const dockerHostGuardsModule = window\.FolderViewPlusDockerHostGuards \|\| null;/);
diff --git a/tests/folder-editor-route-guard.test.mjs b/tests/folder-editor-route-guard.test.mjs
index 9b0eb9d6..491615b7 100644
--- a/tests/folder-editor-route-guard.test.mjs
+++ b/tests/folder-editor-route-guard.test.mjs
@@ -92,7 +92,7 @@ test('folder editor URLs duplicate folder identity into the hash for navigation-
assert.match(dockerRuntimeActionsJs, /const EDITOR_DEBUG_LAUNCH_STORAGE_KEY = 'fv\.folder\.editor\.debug\.launch\.v1';/);
assert.match(dockerRuntimeActionsJs, /const recordFolderEditorLaunchDebug = \(sourcePage, folderType, id, targetUrl\) =>/);
assert.match(dockerRuntimeActionsJs, /win\.name = `\$\{EDITOR_WINDOW_NAME_PREFIX\}\$\{payload\}`;/);
- assert.match(dockerJs, /const editFolder = \(id\) => \{[\s\S]*actionsApi\.editFolder\(id\);/);
+ assert.match(dockerJs, /const editFolder = \(id, options = \{\}\) => getDockerRuntimeActionsApi\(\)\?\.editFolder\?\.\(id, options\);/);
assert.match(vmJs, /const EDITOR_WINDOW_NAME_PREFIX = 'fv\.folder\.editor\.v1:'/);
assert.match(vmJs, /const EDITOR_BOOTSTRAP_COOKIE_NAME = 'fv_folder_editor_bootstrap';/);
assert.match(vmJs, /const EDITOR_DEBUG_LAUNCH_STORAGE_KEY = 'fv\.folder\.editor\.debug\.launch\.v1';/);
diff --git a/tests/folder-editor-settings-contract.test.mjs b/tests/folder-editor-settings-contract.test.mjs
index 476f1cee..847d65bf 100644
--- a/tests/folder-editor-settings-contract.test.mjs
+++ b/tests/folder-editor-settings-contract.test.mjs
@@ -66,7 +66,9 @@ test('modern folder editor schema fields all exist in the page markup and metada
test('modern folder editor load path covers every schema-backed field', () => {
assert.match(folderJs, /populateParentFolderOptions\(/);
- const fieldsLoadedViaBinding = schemaFields.filter((fieldName) => fieldName !== 'parent_folder_id');
+ const fieldsLoadedViaBinding = schemaFields.filter(
+ (fieldName) => !['parent_folder_id', 'webui_profiles'].includes(fieldName)
+ );
fieldsLoadedViaBinding.forEach((fieldName) => {
assert.match(
folderJs,
@@ -74,6 +76,7 @@ test('modern folder editor load path covers every schema-backed field', () => {
`Expected modern folder editor loader to bind ${fieldName}.`
);
});
+ assert.match(folderJs, /getFolderWebuiProfilesApi\(\)\?\.hydrate\(normalizedFolder\.settings\.webui_profiles/);
assert.match(folderJs, /const getFolderEditorStateApi = \(\) =>/);
assert.match(folderJs, /const restoreSectionSavedValues = \(sectionKey\) => \{\s*getFolderEditorStateApi\(\)\?\.restoreSectionSavedValues\(sectionKey\);\s*\};/);
diff --git a/tests/folder-settings-transfer.test.mjs b/tests/folder-settings-transfer.test.mjs
index c5b57113..9d064b95 100644
--- a/tests/folder-settings-transfer.test.mjs
+++ b/tests/folder-settings-transfer.test.mjs
@@ -62,7 +62,8 @@ test('folder settings transfer copies only safe settings payload fields', () =>
icon: 'folder.png',
settings: {
preview: 2,
- override_default_actions: true
+ override_default_actions: true,
+ webui_profiles: [{ id: 'media', name: 'Media', containers: ['one'] }]
},
actions: [
{ type: 1, name: 'Script action', script: 'echo ok' },
@@ -77,6 +78,7 @@ test('folder settings transfer copies only safe settings payload fields', () =>
assert.deepEqual(Object.keys(entry.payload).sort(), ['actions', 'icon', 'settings']);
assert.equal(entry.payload.icon, 'folder.png');
assert.equal(entry.payload.settings.preview, 2);
+ assert.equal(entry.payload.settings.webui_profiles, undefined);
assert.equal(entry.payload.actions.length, 1);
assert.equal(entry.payload.actions[0].name, 'Script action');
assert.deepEqual(Array.from(entry.meta.omittedTopLevelKeys), ['name', 'parentId', 'regex', 'containers', 'id', 'createdAt', 'updatedAt']);
diff --git a/tests/folder-webui-profiles.test.mjs b/tests/folder-webui-profiles.test.mjs
new file mode 100644
index 00000000..a9bfe03e
--- /dev/null
+++ b/tests/folder-webui-profiles.test.mjs
@@ -0,0 +1,61 @@
+import assert from 'node:assert/strict';
+import test from 'node:test';
+import { createRequire } from 'node:module';
+
+const require = createRequire(import.meta.url);
+const profiles = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.webui-profiles.js');
+
+test('normalizes profile identities, names, and selected members', () => {
+ const normalized = profiles.normalizeProfiles([
+ { id: 'media', name: ' Media ', containers: ['Plex', 'Plex', '', 'Tautulli'] },
+ { id: 'media', name: '', members: ['Overseerr'] },
+ null
+ ]);
+ assert.deepEqual(normalized, [
+ { id: 'media', name: 'Media', containers: ['Plex', 'Tautulli'] },
+ { id: 'media-2', name: 'Profile 2', containers: ['Overseerr'] }
+ ]);
+});
+
+test('resolves only currently eligible selected WebUIs and deduplicates URLs', () => {
+ const result = profiles.collectProfileTargets({
+ id: 'media', name: 'Media', containers: ['Plex', 'Tautulli', 'Missing', 'Stopped']
+ }, {
+ Plex: { name: 'Plex', state: true, pause: false, webui: 'http://plex/' },
+ Tautulli: { state: true, pause: false, WebUi: 'http://plex/' },
+ Stopped: { state: false, pause: false, webui: 'http://stopped/' },
+ Other: { state: true, pause: false, webui: 'http://other/' }
+ }, {
+ getSafeWebuiUrl: (value) => /^https?:/.test(value) ? value : '',
+ runningOnly: true
+ });
+ assert.deepEqual(result.urls, ['http://plex/']);
+ assert.equal(result.selectedCount, 4);
+ assert.equal(result.eligibleCount, 1);
+ assert.equal(result.unavailableCount, 1);
+});
+
+test('renames profile targets and strips profiles from transferable settings', () => {
+ assert.deepEqual(profiles.renameProfileMember([
+ { id: 'media', name: 'Media', containers: ['Plex', 'Tautulli'] }
+ ], 'Plex', 'Plex-Media'), [
+ { id: 'media', name: 'Media', containers: ['Plex-Media', 'Tautulli'] }
+ ]);
+ assert.deepEqual(profiles.stripProfilesFromSettings({
+ preview: 2,
+ webui_profiles: [{ id: 'media' }],
+ webuiProfiles: [{ id: 'legacy' }]
+ }), { preview: 2 });
+});
+
+test('diagnostic summary contains counts only', () => {
+ assert.deepEqual(profiles.summarizeProfiles([
+ { id: 'media', name: 'Media', containers: ['Plex', 'Missing'] }
+ ], ['Plex']), {
+ profileCount: 1,
+ selectedReferenceCount: 2,
+ unavailableReferenceCount: 1,
+ duplicateProfileIdCount: 0,
+ invalidProfileCount: 0
+ });
+});
diff --git a/tests/folderviewplus-utils.test.mjs b/tests/folderviewplus-utils.test.mjs
index e1f9de60..013006d9 100644
--- a/tests/folderviewplus-utils.test.mjs
+++ b/tests/folderviewplus-utils.test.mjs
@@ -202,6 +202,24 @@ test('normalizeFolderMap rejects prototype keys and unsafe identifiers', () => {
assert.equal(utils.normalizeFolderId('bad/id'), '');
});
+test('normalizeFolderMap rejects non-string names and members without coercion', () => {
+ const nonCoercible = { toString: 0 };
+ const normalized = utils.normalizeFolderMap({
+ unsafe: {
+ name: nonCoercible,
+ containers: []
+ },
+ safe: {
+ name: ' Safe ',
+ containers: [' plex ', nonCoercible, 42, 'plex', 'sonarr']
+ }
+ });
+
+ assert.deepEqual(Object.keys(normalized), ['safe']);
+ assert.equal(normalized.safe.name, 'Safe');
+ assert.deepEqual(normalized.safe.containers, ['plex', 'sonarr']);
+});
+
test('normalizeFolderMembers is exported and normalizes arrays/objects', () => {
assert.equal(typeof utils.normalizeFolderMembers, 'function');
assert.deepEqual(
@@ -503,20 +521,29 @@ test('orderFoldersByPrefs keeps child folders nested after parent in sorted outp
test('getFolderStatusColors normalizes and defaults values', () => {
const defaults = utils.getFolderStatusColors({});
assert.deepEqual(defaults, {
- started: '#ffffff',
+ started: '#55b72d',
paused: '#b8860b',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d',
+ text: '#ffffff'
});
+ assert.equal(utils.getFolderStatusColors({ status_color_started: '#ffffff' }).started, '#55b72d');
+ assert.equal(utils.getFolderStatusColors({
+ status_color_started: '#ffffff',
+ status_color_started_explicit: true
+ }).started, '#ffffff');
+
const custom = utils.getFolderStatusColors({
status_color_started: '#AbC',
status_color_paused: '#123456',
- status_color_stopped: 'bad-value'
+ status_color_stopped: 'bad-value',
+ status_color_text: '#fed'
});
assert.deepEqual(custom, {
started: '#aabbcc',
paused: '#123456',
- stopped: '#ff4d4d'
+ stopped: '#ff4d4d',
+ text: '#ffeedd'
});
});
@@ -931,7 +958,11 @@ test('buildImportDiffRows reports row-level changed fields', () => {
test('export/import roundtrip smoke works for full payload', () => {
const original = {
- abc: { name: 'One', containers: ['x'] },
+ abc: {
+ name: 'One',
+ containers: ['x'],
+ settings: { webui_profiles: [{ id: 'tools', name: 'Tools', containers: ['x'] }] }
+ },
def: { name: 'Two', containers: [] }
};
const exported = utils.buildFullExportPayload({
@@ -945,6 +976,7 @@ test('export/import roundtrip smoke works for full payload', () => {
assert.deepEqual(Object.keys(parsed.folders), ['abc', 'def']);
assert.equal(parsed.folders.abc.name, 'One');
assert.deepEqual(parsed.folders.abc.containers, ['x']);
+ assert.deepEqual(parsed.folders.abc.settings.webui_profiles, original.abc.settings.webui_profiles);
assert.equal(parsed.folders.def.name, 'Two');
assert.deepEqual(parsed.folders.def.containers, []);
@@ -986,12 +1018,14 @@ test('getConflictReport detects multi-folder assignment conflicts', () => {
assert.equal(plex.matchedFolderCount, 2);
});
-test('normalizePrefs keeps pinned folders and hide-empty toggle', () => {
+test('normalizePrefs keeps pinned, hidden, and hide-empty folder preferences', () => {
const prefs = utils.normalizePrefs({
pinnedFolderIds: ['a', 'b', 'a', '', 'c'],
+ hiddenFolderIds: ['hidden-a', 'hidden-b', 'hidden-a', ''],
hideEmptyFolders: true
});
assert.deepEqual(prefs.pinnedFolderIds, ['a', 'b', 'c']);
+ assert.deepEqual(prefs.hiddenFolderIds, ['hidden-a', 'hidden-b']);
assert.equal(prefs.hideEmptyFolders, true);
});
diff --git a/tests/localization-roadmap.test.mjs b/tests/localization-roadmap.test.mjs
index ef860eb0..1beb3001 100644
--- a/tests/localization-roadmap.test.mjs
+++ b/tests/localization-roadmap.test.mjs
@@ -75,7 +75,7 @@ test('catalog report exposes honest legacy and namespace coverage for every regi
assert.ok(report.sourceMessageCount > 1900);
assert.equal(report.namespaceCount, 10);
assert.equal(report.extraction.candidateCount, 0);
- assert.equal(report.extraction.autoBoundMessageCount, 1574);
+ assert.equal(report.extraction.autoBoundMessageCount, 1579);
assert.equal(report.extraction.catalogMessageCount, report.sourceMessageCount);
assert.equal(Object.keys(report.locales).length, 27);
assert.equal(report.locales.en.coveragePercent, 100);
diff --git a/tests/localization-surface-coverage.test.mjs b/tests/localization-surface-coverage.test.mjs
index 060c7cba..e038ae03 100644
--- a/tests/localization-surface-coverage.test.mjs
+++ b/tests/localization-surface-coverage.test.mjs
@@ -19,7 +19,7 @@ test('every detected legacy UI phrase is represented by one stable generated key
const english = messagesOnly(readJson(path.join(namespacesRoot, 'en/legacy-surface.json')));
const expected = Object.fromEntries([...surface.byPhrase.keys()].sort().map((phrase) => [surfaceTools.keyForPhrase(phrase), phrase]));
- assert.equal(surface.byPhrase.size, 1574);
+ assert.equal(surface.byPhrase.size, 1579);
assert.deepEqual(english, expected);
assert.equal(new Set(Object.keys(english)).size, surface.byPhrase.size);
});
@@ -49,8 +49,8 @@ test('the extraction report and runtime enforce zero-debt initial and dynamic co
assert.equal(report['catalog-version'], '2026.08.10.1');
assert.equal(report['candidate-count'], 0);
- assert.equal(report['auto-bound-message-count'], 1574);
- assert.equal(report['catalog-message-count'], 2122);
+ assert.equal(report['auto-bound-message-count'], 1579);
+ assert.equal(report['catalog-message-count'], 2168);
assert.match(runtime, /rebuildAutoPhraseIndex/);
assert.match(runtime, /resolveAutoTranslation/);
assert.match(runtime, /observeDynamicTranslations/);
diff --git a/tests/member-identity-reconciliation.test.mjs b/tests/member-identity-reconciliation.test.mjs
index 0795b43a..08028851 100644
--- a/tests/member-identity-reconciliation.test.mjs
+++ b/tests/member-identity-reconciliation.test.mjs
@@ -23,7 +23,10 @@ test('a uniquely identified renamed Docker member keeps folder state and action
mountDestinations: ['/config', '/tv']
}
},
- actions: [{ type: 0, containers: ['old-name'], conatiners: ['old-name'] }]
+ actions: [{ type: 0, containers: ['old-name'], conatiners: ['old-name'] }],
+ settings: {
+ webui_profiles: [{ id: 'media', name: 'Media tools', containers: ['old-name'] }]
+ }
}
};
const runtime = {
@@ -41,6 +44,7 @@ test('a uniquely identified renamed Docker member keeps folder state and action
assert.deepEqual(result.folders.media.hiddenPreviewMembers, ['new-name']);
assert.deepEqual(result.folders.media.actions[0].containers, ['new-name']);
assert.deepEqual(result.folders.media.actions[0].conatiners, ['new-name']);
+ assert.deepEqual(result.folders.media.settings.webui_profiles[0].containers, ['new-name']);
assert.equal(result.diagnostics.renamed, 1);
assert.equal(result.patches.media.renames['old-name'], 'new-name');
});
diff --git a/tests/package-trust-security.test.mjs b/tests/package-trust-security.test.mjs
index 9962e28c..75f81936 100644
--- a/tests/package-trust-security.test.mjs
+++ b/tests/package-trust-security.test.mjs
@@ -29,7 +29,9 @@ test('manifest and sidecar bind the current core package with SHA-256', () => {
});
test('stable release publishes pinned provenance and SBOM attestations', () => {
- assert.match(workflow, /permissions:\s*\n\s*contents:\s*write\s*\n\s*id-token:\s*write\s*\n\s*attestations:\s*write/);
+ assert.match(workflow, /^permissions:\s*\n contents:\s*read\s*$/m);
+ const releaseJob = workflow.match(/^ release:\s*$([\s\S]*?)(?=^ [A-Za-z0-9_-]+:\s*$|(?![\s\S]))/m)?.[1] || '';
+ assert.match(releaseJob, /permissions:\s*\n\s*contents:\s*write\s*\n\s*id-token:\s*write\s*\n\s*attestations:\s*write/);
const attestUses = workflow.match(/uses:\s*actions\/attest@[0-9a-f]{40}\s+# v4\.2\.0/g) || [];
assert.equal(attestUses.length, 2);
assert.match(workflow, /Attest release archive provenance[\s\S]*subject-path:[\s\S]*archive[\s\S]*checksum/);
diff --git a/tests/performance-optimizations.test.mjs b/tests/performance-optimizations.test.mjs
index 7cc17f3b..5fd59220 100644
--- a/tests/performance-optimizations.test.mjs
+++ b/tests/performance-optimizations.test.mjs
@@ -400,7 +400,8 @@ test('docker preview popup runtime stays enabled behind lazy advanced-preview in
assert.match(dockerJs, /initializeDockerTooltipOnDemand\(\$\(tooltip_trigger_element\), \(\) => \$\(tooltip_trigger_element\)\.tooltipster\(\{/);
assert.match(dockerJs, /\}\), triggerMode === 'hover'\);/);
assert.match(dockerJs, /if \(DOCKER_PREVIEW_POPUP_ENABLED !== true\) \{\s*return;\s*\}/);
- assert.match(dockerJs, /if\(DOCKER_PREVIEW_POPUP_ENABLED && tooltip_trigger_element && tooltip_trigger_element\.length > 0\) \{/);
+ assert.match(dockerJs, /const bindAdvancedPreviewContext = \(\$contextTrigger, context = \{\}\) =>/);
+ assert.match(dockerJs, /if\(DOCKER_PREVIEW_POPUP_ENABLED && tooltipTarget\.length > 0\) \{/);
});
test('docker first paint keeps a lightweight loading shell and enriches state payload fields', () => {
diff --git a/tests/phase6-php-decomposition.test.mjs b/tests/phase6-php-decomposition.test.mjs
index dea83e82..43c51797 100644
--- a/tests/phase6-php-decomposition.test.mjs
+++ b/tests/phase6-php-decomposition.test.mjs
@@ -40,7 +40,7 @@ test('Phase 6 preserves the complete public endpoint manifest', () => {
});
test('facades load every contracted PHP module without retaining extracted implementations', () => {
- assert.equal(architecture.serverModuleContracts.length, 29);
+ assert.equal(architecture.serverModuleContracts.length, 30);
for (const contract of architecture.serverModuleContracts) {
const loader = contract.loadedBy === 'server/lib.php'
? coreFacade
@@ -56,7 +56,7 @@ test('facades load every contracted PHP module without retaining extracted imple
test('decomposition preserves every historical public PHP function name', () => {
const expected = {
- 'server/lib.php': { count: 332, sha256: '3402a8afdb4a90878b5a5cc26284075606051f35819e2859df9bc23a2b8612c3' },
+ 'server/lib.php': { count: 337, sha256: 'bfa36d3deb8611f25234f639c900b8a97d50c3567a11d3b6f1c70339f4d93d04' },
'server/lib.diagnostics.php': { count: 74, sha256: 'a1d008eb74affc973be697a63837347f992ff932591d6aa630b2a4d022a24b1a' },
'server/upload_custom_icon.php': { count: 56, sha256: '825b3fbb5288c76f83a4976a55967e8a9a205e5496581f3a0c842789083f530e' }
};
diff --git a/tests/phase8-test-runner-decomposition.test.mjs b/tests/phase8-test-runner-decomposition.test.mjs
index 602aa972..f726e308 100644
--- a/tests/phase8-test-runner-decomposition.test.mjs
+++ b/tests/phase8-test-runner-decomposition.test.mjs
@@ -20,8 +20,8 @@ test('Phase 8 runner contract preserves deterministic fixture intent inventory',
cwd: rootDir,
encoding: 'utf8'
});
- assert.match(output, /43 ordered fixture cases/);
- assert.equal(contract.intent.fixture.assertionCount, 557);
+ assert.match(output, /49 ordered fixture cases/);
+ assert.equal(contract.intent.fixture.assertionCount, 617);
});
test('Phase 8 change classification covers every extracted runner family', async () => {
diff --git a/tests/runtime-page-diagnostics.test.mjs b/tests/runtime-page-diagnostics.test.mjs
index b7374f18..c141adaf 100644
--- a/tests/runtime-page-diagnostics.test.mjs
+++ b/tests/runtime-page-diagnostics.test.mjs
@@ -18,13 +18,24 @@ const createFixture = () => {
const nodes = {
'tr': [{}, {}],
'.folder, .folder-showcase-outer': [{}],
+ '.folder.fv-folder-hidden-revealed': [{}, {}],
'.folder[expanded="true"], .folder-showcase-outer[expanded="true"]': [{}],
'.folder-preview-wrapper, .folder-element-docker, .folder-element-vm': [{}, {}, {}],
'.spinner, .fv-runtime-loading-row, [aria-busy="true"]': [],
'.fa-spin, .fa-spinner, .fa-circle-o-notch': [{}],
'.error, .invalid, [aria-invalid="true"]': []
};
- const host = { clientWidth: 100, scrollWidth: 120, querySelectorAll: (selector) => nodes[selector] || [] };
+ const bodyAttributes = new Map([
+ ['data-fvplus-docker-hidden-explicit', '2'],
+ ['data-fvplus-docker-hidden-effective', '3'],
+ ['data-fvplus-docker-hidden-reveal', 'true']
+ ]);
+ const host = {
+ clientWidth: 100,
+ scrollWidth: 120,
+ querySelectorAll: (selector) => nodes[selector] || [],
+ getAttribute: (name) => bodyAttributes.get(name) || null
+ };
const document = {
body: host,
documentElement: { clientWidth: 1200, scrollWidth: 1200 },
@@ -55,6 +66,10 @@ test('runtime page diagnostics captures allowlisted aggregate state only', () =>
});
assert.equal(snapshot.state.visibleRows, 2);
assert.equal(snapshot.state.spinningControls, 1);
+ assert.equal(snapshot.state.hiddenFolderSelections, 2);
+ assert.equal(snapshot.state.effectivelyHiddenFolders, 3);
+ assert.equal(snapshot.state.revealedHiddenFolderRows, 2);
+ assert.equal(snapshot.state.revealHiddenFolders, true);
assert.equal(snapshot.state.horizontalOverflow, true);
assert.equal(snapshot.viewport.widthBucket, '1025-1440');
const serialized = JSON.stringify(snapshot);
diff --git a/tests/runtime-theme-token-guard.test.mjs b/tests/runtime-theme-token-guard.test.mjs
index 8c42868c..dbe1c559 100644
--- a/tests/runtime-theme-token-guard.test.mjs
+++ b/tests/runtime-theme-token-guard.test.mjs
@@ -49,9 +49,9 @@ test('runtime css defines canonical fvplus status tokens and legacy graph aliase
});
test('status state classes resolve through css variables instead of hardcoded runtime values', () => {
- assert.match(dockerCss, /span\.folder-state\.fv-folder-state-started\s*\{[\s\S]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
+ assert.match(dockerCss, /span\.folder-state\.fv-folder-state-started\s*\{[\s\S]*var\(--fvplus-folder-status-text,\s*#ffffff\)/);
assert.match(dockerCss, /i\.folder-load-status\.started\s*\{[\s\S]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
- assert.match(vmCss, /span\.folder-state\.fv-folder-state-started\s*\{[\s\S]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
+ assert.match(vmCss, /span\.folder-state\.fv-folder-state-started\s*\{[\s\S]*var\(--fvplus-folder-status-text,\s*#ffffff\)/);
assert.match(vmCss, /i\.folder-load-status\.started\s*\{[\s\S]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
});
@@ -95,6 +95,8 @@ test('runtime context menus follow resolved dark and light theme tokens', () =>
test('runtime scripts avoid inline status color painting and use row-level css variable overrides', () => {
assert.match(sharedRuntimeJs, /const FOLDER_STATUS_COLOR_STYLE_PROPS = Object\.freeze/);
+ assert.match(sharedRuntimeJs, /text:\s*'--fvplus-folder-status-text'/);
+ assert.match(sharedRuntimeJs, /text:\s*normalizeStatusHexColor\(source\.status_color_text,\s*DEFAULT_FOLDER_STATUS_COLORS\.text\)/);
assert.match(sharedRuntimeJs, /const applyFolderStatusColorOverrides = \(\$folderRow, settings\) =>/);
assert.match(dockerJs, /applyFolderStatusColorOverrides\(\$folderRow,\s*folder\.settings\)/);
assert.match(vmJs, /const applyFolderStatusColorOverrides = typeof runtimeShared\.applyFolderStatusColorOverrides === 'function'/);
@@ -104,6 +106,7 @@ test('runtime scripts avoid inline status color painting and use row-level css v
assert.match(dashboardJs, /const resolveDashboardFolderStatusColors = \(settings\) => \{/);
assert.match(dashboardJs, /started:\s*'var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started,\s*var\(--fvplus-theme-foreground,\s*currentColor\)\)\)'/);
assert.match(dashboardJs, /const statusColors = resolveDashboardFolderStatusColors\(folder\.settings\);/);
+ assert.match(dashboardJs, /\$statusText\.text\([^;]+\)\.css\('color',\s*statusColors\.text\)/);
});
test('theme-change observers trigger deterministic reflow across runtime and settings surfaces', () => {
@@ -253,6 +256,7 @@ test('runtime and settings overlays resolve through theme tokens instead of hard
assert.match(dockerCss, /\.fv-preview-status-started\s*\{[^}]*var\(--fvplus-folder-status-started,\s*var\(--fvplus-status-started\)\)/);
assert.match(dockerCss, /\.fv-preview-status-paused\s*\{[^}]*var\(--fvplus-folder-status-paused,\s*var\(--fvplus-status-paused\)\)/);
assert.match(dockerCss, /\.fv-preview-status-stopped\s*\{[^}]*var\(--fvplus-folder-status-stopped,\s*var\(--fvplus-status-stopped\)\)/);
+ assert.match(dockerCss, /\.folder-preview \.fv-preview-status-compact \.state,[\s\S]*var\(--fvplus-folder-status-text,\s*#ffffff\)/);
assert.match(cspUtilitiesCss, /\.fv-popup-guide\s*\{[^}]*color:\s*var\(--fvplus-runtime-menu-fg,\s*var\(--fvplus-theme-foreground,\s*currentColor\)\)/);
assert.match(cspUtilitiesCss, /\.fv-popup-panel\s*\{[^}]*background:\s*var\(--fvplus-runtime-menu-header-bg,\s*transparent\)/);
assert.doesNotMatch(dockerRuntimeActionsJs, /color:\s*#e8edf7/);
diff --git a/tests/security-property-fuzz.test.mjs b/tests/security-property-fuzz.test.mjs
new file mode 100644
index 00000000..2cf6e131
--- /dev/null
+++ b/tests/security-property-fuzz.test.mjs
@@ -0,0 +1,143 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { createRequire } from 'node:module';
+import fc from 'fast-check';
+import {
+ compareSanitizedSupportBundles,
+ renderSupportBundleComparisonMarkdown
+} from '../scripts/lib/support_bundle_compare.mjs';
+
+const require = createRequire(import.meta.url);
+const imageFallbacks = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/runtime.image-fallbacks.js');
+globalThis.FolderViewPlusFoundationModules = { imageFallbacks };
+const utils = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folderviewplus.utils.js');
+const containerModel = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/docker.runtime.container-model.js');
+const hierarchy = require('../src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/scripts/folder.editor.hierarchy.js').createApi();
+
+const FUZZ_OPTIONS = Object.freeze({ numRuns: 250 });
+
+const folderRecordArbitrary = fc.record({
+ name: fc.oneof(fc.string({ maxLength: 80 }), fc.jsonValue()),
+ icon: fc.oneof(fc.string({ maxLength: 9000 }), fc.jsonValue()),
+ parentId: fc.oneof(fc.string({ maxLength: 80 }), fc.jsonValue()),
+ containers: fc.oneof(
+ fc.array(fc.jsonValue(), { maxLength: 40 }),
+ fc.dictionary(fc.string({ maxLength: 40 }), fc.boolean(), { maxKeys: 40 }),
+ fc.jsonValue()
+ ),
+ settings: fc.jsonValue(),
+ actions: fc.oneof(fc.array(fc.jsonValue(), { maxLength: 240 }), fc.jsonValue())
+});
+
+test('fast-check: folder normalization preserves identifier and collection safety invariants', () => {
+ fc.assert(fc.property(
+ fc.dictionary(fc.string({ maxLength: 80 }), folderRecordArbitrary, { maxKeys: 50 }),
+ (source) => {
+ const normalized = utils.normalizeFolderMap(source);
+ assert.equal(Object.getPrototypeOf(normalized), Object.prototype);
+ for (const [id, folder] of Object.entries(normalized)) {
+ assert.equal(utils.normalizeFolderId(id), id);
+ assert.notEqual(id, '__proto__');
+ assert.notEqual(id, 'constructor');
+ assert.equal(folder.name, folder.name.trim());
+ assert.notEqual(folder.name, '');
+ assert.ok(Array.isArray(folder.containers));
+ assert.deepEqual(folder.containers, [...new Set(folder.containers)]);
+ assert.ok(folder.containers.every((value) => typeof value === 'string' && value.trim() !== ''));
+ assert.ok(Array.isArray(folder.actions));
+ assert.ok(folder.actions.length <= 200);
+ assert.equal(typeof folder.settings, 'object');
+ assert.equal(Array.isArray(folder.settings), false);
+ }
+ }
+ ), FUZZ_OPTIONS);
+});
+
+test('fast-check: import parsing is total for arbitrary JSON and returns a bounded contract', () => {
+ fc.assert(fc.property(
+ fc.jsonValue(),
+ fc.constantFrom('docker', 'vm'),
+ (payload, expectedType) => {
+ const parsed = utils.parseImportPayload(payload, expectedType);
+ assert.equal(typeof parsed, 'object');
+ assert.equal(typeof parsed.ok, 'boolean');
+ if (parsed.ok) {
+ assert.equal(typeof parsed.folders, 'object');
+ assert.equal(Array.isArray(parsed.folders), false);
+ assert.ok(Object.keys(parsed.folders).every((id) => utils.normalizeFolderId(id) === id));
+ } else {
+ assert.equal(typeof parsed.error, 'string');
+ assert.notEqual(parsed.error.trim(), '');
+ }
+ }
+ ), FUZZ_OPTIONS);
+});
+
+test('fast-check: cyclic folder graphs produce bounded descendant sets', () => {
+ fc.assert(fc.property(
+ fc.array(fc.integer({ min: -1, max: 40 }), { minLength: 1, maxLength: 40 }),
+ fc.nat(),
+ (parentIndexes, rootSeed) => {
+ const ids = parentIndexes.map((_value, index) => `folder-${index}`);
+ const folders = Object.fromEntries(ids.map((id, index) => {
+ const parentIndex = parentIndexes[index];
+ const parentId = parentIndex >= 0 ? ids[parentIndex % ids.length] : '';
+ return [id, { name: id, parentId }];
+ }));
+ const rootId = ids[rootSeed % ids.length];
+ const descendants = hierarchy.computeFolderDescendantIds(folders, rootId);
+ assert.ok(descendants instanceof Set);
+ assert.equal(descendants.has(rootId), false);
+ assert.ok(descendants.size <= ids.length - 1);
+ assert.ok([...descendants].every((id) => Object.hasOwn(folders, id)));
+ }
+ ), FUZZ_OPTIONS);
+});
+
+test('fast-check: Docker container normalization returns immutable bounded structures', () => {
+ fc.assert(fc.property(
+ fc.dictionary(fc.string({ maxLength: 40 }), fc.jsonValue(), { maxKeys: 50 }),
+ fc.string({ maxLength: 40 }),
+ (entry, source) => {
+ const normalized = containerModel.normalizeContainer(entry, { source });
+ assert.equal(normalized.schemaVersion, 1);
+ assert.ok(Object.isFrozen(normalized));
+ assert.ok(Object.isFrozen(normalized.names));
+ assert.ok(Object.isFrozen(normalized.ports));
+ assert.ok(Object.isFrozen(normalized.mounts));
+ assert.equal(normalized.name.startsWith('/'), false);
+ assert.ok(normalized.ports.every((port) => Object.isFrozen(port)));
+ assert.ok(normalized.mounts.every((mount) => Object.isFrozen(mount)));
+ }
+ ), FUZZ_OPTIONS);
+});
+
+test('fast-check: support-bundle comparison omits values behind sensitive keys', () => {
+ fc.assert(fc.property(
+ fc.stringMatching(/^[a-zA-Z0-9]{1,32}$/),
+ fc.nat({ max: 10000 }),
+ fc.nat({ max: 10000 }),
+ (suffix, leftCount, rightCount) => {
+ const marker = `private-${suffix}`;
+ const makeBundle = (folderCount) => ({
+ bundleMeta: { privacyMode: 'sanitized', schemaVersion: 2 },
+ pluginState: {
+ folderCount,
+ folderName: marker,
+ configPath: marker,
+ sourceUrl: marker,
+ ipAddress: marker,
+ accessToken: marker,
+ sessionCookie: marker,
+ machineUuid: marker
+ },
+ redactionManifest: { mode: 'sanitized', hashedCount: 8 }
+ });
+ const report = compareSanitizedSupportBundles(makeBundle(leftCount), makeBundle(rightCount));
+ const rendered = renderSupportBundleComparisonMarkdown(report);
+ assert.doesNotMatch(JSON.stringify(report), new RegExp(marker));
+ assert.doesNotMatch(rendered, new RegExp(marker));
+ assert.ok(report.differences.every((row) => !/(name|path|url|ip|token|cookie|uuid)/i.test(row.path)));
+ }
+ ), FUZZ_OPTIONS);
+});
diff --git a/tests/security-release-contract.test.mjs b/tests/security-release-contract.test.mjs
index 380d222d..ab880ccc 100644
--- a/tests/security-release-contract.test.mjs
+++ b/tests/security-release-contract.test.mjs
@@ -16,6 +16,52 @@ test('CodeQL scans dev and main for pushes and pull requests', () => {
assert.match(workflow, /workflow_dispatch:/);
assert.match(workflow, /queries: security-extended,security-and-quality/);
assert.equal((workflow.match(/github\/codeql-action\/(?:init|autobuild|analyze)@[0-9a-f]{40}\s+# v4/g) || []).length, 3);
+ assert.match(workflow, /^permissions:\s*\n actions:\s*read\s*\n contents:\s*read\s*$/m);
+ const analyzeJob = workflow.match(/^ analyze:\s*$([\s\S]*?)(?=^ [A-Za-z0-9_-]+:\s*$|(?![\s\S]))/m)?.[1] || '';
+ assert.match(analyzeJob, /permissions:\s*\n\s*actions:\s*read\s*\n\s*contents:\s*read\s*\n\s*security-events:\s*write/);
+});
+
+test('write-capable workflows keep top-level permissions read-only and scope writes to jobs', () => {
+ const contracts = [
+ {
+ file: 'backmerge-main-to-dev.yml',
+ job: 'backmerge',
+ permissionPattern: /permissions:\s*\n\s*contents:\s*write\s*\n\s*pull-requests:\s*write/
+ },
+ {
+ file: 'release-on-main.yml',
+ job: 'release',
+ permissionPattern: /permissions:\s*\n\s*contents:\s*write\s*\n\s*id-token:\s*write\s*\n\s*attestations:\s*write/
+ },
+ {
+ file: 'clone-traffic-badge.yml',
+ job: 'publish',
+ permissionPattern: /permissions:\s*\n\s*contents:\s*write/
+ }
+ ];
+ for (const contract of contracts) {
+ const workflow = read(path.join('.github', 'workflows', contract.file));
+ assert.match(workflow, /^permissions:\s*\n contents:\s*read\s*$/m, contract.file);
+ const job = workflow.match(new RegExp(`^ ${contract.job}:\\s*$([\\s\\S]*?)(?=^ [A-Za-z0-9_-]+:\\s*$|(?![\\s\\S]))`, 'm'))?.[1] || '';
+ assert.match(job, contract.permissionPattern, contract.file);
+ }
+});
+
+test('clone traffic credential is isolated from metrics branch publication', () => {
+ const workflow = read('.github/workflows/clone-traffic-badge.yml');
+ const collectJob = workflow.match(/^ collect:\s*$([\s\S]*?)(?=^ [A-Za-z0-9_-]+:\s*$|(?![\s\S]))/m)?.[1] || '';
+ const publishJob = workflow.match(/^ publish:\s*$([\s\S]*?)(?=^ [A-Za-z0-9_-]+:\s*$|(?![\s\S]))/m)?.[1] || '';
+ assert.match(collectJob, /secrets\.FVPLUS_TRAFFIC_TOKEN/);
+ assert.doesNotMatch(collectJob, /github\.token/);
+ assert.match(publishJob, /github\.token/);
+ assert.doesNotMatch(publishJob, /secrets\.FVPLUS_TRAFFIC_TOKEN/);
+ assert.match(publishJob, /needs:\s*collect/);
+});
+
+test('security policy links directly to private vulnerability reporting', () => {
+ const policy = read('.github/SECURITY.md');
+ const policyLines = new Set(policy.split(/\r?\n/));
+ assert.ok(policyLines.has('[Report a vulnerability](https://github.com/alexphillips-dev/FolderView-Plus/security/advisories/new):'));
});
test('dependency review blocks vulnerable or unapproved dependency changes', () => {
diff --git a/tests/settings-bindings.test.mjs b/tests/settings-bindings.test.mjs
index 2bf4b9c8..9a438daa 100644
--- a/tests/settings-bindings.test.mjs
+++ b/tests/settings-bindings.test.mjs
@@ -459,7 +459,8 @@ test('empty-state actions are delegated and create-folder uses modal input inste
assert.match(script, /STARTER_TEMPLATE_BLUEPRINTS = Object\.freeze/);
assert.match(script, /settings:\s*\{[\s\S]*preview:\s*1/);
assert.match(script, /preview_border:\s*true/);
- assert.match(script, /status_color_started:\s*'#ffffff'/);
+ assert.match(script, /status_color_started:\s*'#55b72d'/);
+ assert.match(script, /status_color_text:\s*'#ffffff'/);
assert.match(script, /dashboard_overflow:\s*'default'/);
assert.doesNotMatch(script, /window\.prompt\('Folder name:'/);
});
diff --git a/tests/support-bundle-browser-telemetry.test.mjs b/tests/support-bundle-browser-telemetry.test.mjs
index e3b98e48..274c0969 100644
--- a/tests/support-bundle-browser-telemetry.test.mjs
+++ b/tests/support-bundle-browser-telemetry.test.mjs
@@ -823,6 +823,16 @@ test('support bundle telemetry exports privacy-safe persisted preview context br
counters: { handlerIntegrityFailures: 0, dispatchAttempts: 2, dispatchSuccesses: 2 },
rowModes: { '2': { bindings: 6 }, unlimited: { bindings: 6 } },
rowIndexes: { '1': { bound: 6 }, '2': { bound: 6 } },
+ childFolderPreview: {
+ counters: { chipsRendered: 1, bindings: 1, menuOpenAttempts: 3, menuOpens: 3, menuOpenFailures: 0 },
+ inputMethods: { mouse: 1, keyboard: 1, contextmenu: 1, touch: 0, unknown: 0 },
+ lastEvent: {
+ type: 'menu-open',
+ outcome: 'success',
+ inputMethod: 'contextmenu',
+ folderName: 'private-folder'
+ }
+ },
lastEvent: {
type: 'dispatch',
outcome: 'success',
@@ -847,6 +857,9 @@ test('support bundle telemetry exports privacy-safe persisted preview context br
assert.equal(evidence.lastEvent.rowIndex, 2);
assert.equal(evidence.lastEvent.triggerSource, 'status');
assert.equal(evidence.lastEvent.inputMethod, 'keyboard');
+ assert.equal(evidence.childFolderPreview.counters.menuOpens, 3);
+ assert.equal(evidence.childFolderPreview.inputMethods.contextmenu, 1);
+ assert.match(evidence.childFolderPreview.lastEvent.folderName, /^ui-[0-9a-f]{16}$/);
assert.match(evidence.lastEvent.containerName, /^ui-[0-9a-f]{16}$/);
- assert.doesNotMatch(JSON.stringify(evidence), /private-container/);
+ assert.doesNotMatch(JSON.stringify(evidence), /private-container|private-folder/);
});
diff --git a/tests/support-bundle-v2-contract.test.mjs b/tests/support-bundle-v2-contract.test.mjs
index 3efe2bbc..f0258897 100644
--- a/tests/support-bundle-v2-contract.test.mjs
+++ b/tests/support-bundle-v2-contract.test.mjs
@@ -23,7 +23,8 @@ const FVPLUS_API_ERROR_LOG = ${phpSingleQuote(apiErrorLogPath)};
const FVPLUS_DEFAULT_FOLDER_STATUS_COLORS = [
'started' => '#00ff00',
'paused' => '#f5c100',
- 'stopped' => '#ff4d4f'
+ 'stopped' => '#ff4d4f',
+ 'text' => '#ffffff'
];
function readInstalledManifestPathCandidates(): array {
diff --git a/tests/webui-profile-server-contract.test.mjs b/tests/webui-profile-server-contract.test.mjs
new file mode 100644
index 00000000..c49f3d5f
--- /dev/null
+++ b/tests/webui-profile-server-contract.test.mjs
@@ -0,0 +1,44 @@
+import assert from 'node:assert/strict';
+import { execFileSync } from 'node:child_process';
+import path from 'node:path';
+import test from 'node:test';
+
+const phpPath = path.resolve('src/folderview.plus/usr/local/emhttp/plugins/folderview.plus/server/lib.webui-profiles.php');
+const phpQuote = (value) => `'${String(value).replaceAll('\\', '/').replaceAll("'", "\\'")}'`;
+const runPhp = (expression) => JSON.parse(execFileSync('php', ['-r',
+ `require ${phpQuote(phpPath)}; echo json_encode(${expression}, JSON_UNESCAPED_SLASHES);`
+], { encoding: 'utf8' }));
+
+test('server normalizes WebUI profiles and enforces unique identities and names', () => {
+ const result = runPhp(`fvplusNormalizeWebuiProfiles([
+ ['id'=>'media','name'=>'Media','containers'=>['Plex','Plex','Tautulli']],
+ ['id'=>'media','name'=>'Media','members'=>['Overseerr']]
+ ])`);
+ assert.deepEqual(result, [
+ { id: 'media', name: 'Media', containers: ['Plex', 'Tautulli'] },
+ { id: 'media-2', name: 'Media 2', containers: ['Overseerr'] }
+ ]);
+});
+
+test('server diagnostics expose aggregate counts without profile or member values', () => {
+ const result = runPhp(`fvplusBuildWebuiProfileDiagnostics([
+ 'webui_profiles'=>[['id'=>'media','name'=>'Media','containers'=>['Plex','Missing']]]
+ ], ['Plex'])`);
+ assert.deepEqual(result, {
+ profileCount: 1,
+ selectedReferenceCount: 2,
+ unavailableReferenceCount: 1,
+ duplicateProfileIdCount: 0,
+ invalidProfileCount: 0
+ });
+ assert.doesNotMatch(JSON.stringify(result), /Plex|Missing|Media/);
+});
+
+test('generic settings transfer strips member-bound WebUI profiles', () => {
+ const result = runPhp(`fvplusStripWebuiProfilesFromSettings([
+ 'preview'=>2,
+ 'webui_profiles'=>[['id'=>'media']],
+ 'webuiProfiles'=>[['id'=>'legacy']]
+ ])`);
+ assert.deepEqual(result, { preview: 2 });
+});