mirror of
https://github.com/gtsteffaniak/filebrowser.git
synced 2026-11-04 08:42:06 -05:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6a4b435d6 | ||
|
|
470f3fad92 | ||
|
|
129168c691 | ||
|
|
d4fcfe62d5 |
No files matched your search
@@ -9,7 +9,6 @@ backend/filebrowser.sqlite*
|
||||
backend/tmp
|
||||
backend/dist
|
||||
backend/backend*
|
||||
backend/internal/web/dist/**
|
||||
backend/vendor/**
|
||||
|
||||
frontend/dist/**
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the comma-separated Docker tag list for a release image.
|
||||
#
|
||||
# Usage: docker-release-tags.sh <metadata.json> <variant> <update_track> <update_major>
|
||||
# metadata.json JSON output of docker/metadata-action (uses .tags)
|
||||
# variant default | slim | media
|
||||
# default and media: floating tags include latest
|
||||
# slim: every tag gets a -slim suffix and latest is not added
|
||||
# update_track true when the semver policy allows floating beta/stable/latest tags
|
||||
# update_major true when the semver policy allows major-only version tags
|
||||
#
|
||||
# Image repositories are fixed per variant so that each image only gets its own tags.
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 4 ]]; then
|
||||
echo "usage: $0 <metadata.json> <default|slim|media> <true|false> <true|false>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
metadata_file=$1
|
||||
variant=$2
|
||||
update_track=$3
|
||||
update_major=$4
|
||||
|
||||
case "$variant" in
|
||||
default)
|
||||
suffix=""
|
||||
add_latest=true
|
||||
repos=(gtstef/filebrowser ghcr.io/gtsteffaniak/filebrowser)
|
||||
;;
|
||||
slim)
|
||||
suffix="-slim"
|
||||
add_latest=false
|
||||
repos=(gtstef/filebrowser ghcr.io/gtsteffaniak/filebrowser)
|
||||
;;
|
||||
media)
|
||||
suffix=""
|
||||
add_latest=true
|
||||
repos=(gtstef/filebrowser-media ghcr.io/gtsteffaniak/filebrowser-media)
|
||||
;;
|
||||
*)
|
||||
echo "unknown variant: $variant" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
jq -r \
|
||||
--arg suffix "$suffix" \
|
||||
--argjson addLatest "$add_latest" \
|
||||
--argjson updateTrack "$update_track" \
|
||||
--argjson updateMajor "$update_major" \
|
||||
--arg dh "${repos[0]}" \
|
||||
--arg gh "${repos[1]}" '
|
||||
# Strip the v prefix, drop metadata latest, and append the variant suffix.
|
||||
.tags |= map(
|
||||
sub(":v"; ":")
|
||||
| select(test(":latest") | not)
|
||||
| . + $suffix
|
||||
) |
|
||||
# Version tags with a prerelease suffix (X.Y.Z-suffix) get major.minor, and major when allowed.
|
||||
.tags += [
|
||||
.tags[]
|
||||
| select(test(":[0-9]+\\.[0-9]+\\.[0-9]+-"))
|
||||
| split(":")
|
||||
| .[-1] as $version
|
||||
| ($version | split("-")) as $version_split
|
||||
| ($version_split[0] | split(".")) as $version_parts
|
||||
| ($version_split[1:] | join("-")) as $tag_suffix
|
||||
| ($version_parts | "\(.[0]).\(.[1])") as $major_minor
|
||||
| ($version_parts[0]) as $major
|
||||
| "\($dh):\($major_minor)-\($tag_suffix)",
|
||||
"\($gh):\($major_minor)-\($tag_suffix)",
|
||||
if $updateMajor then
|
||||
"\($dh):\($major)-\($tag_suffix)",
|
||||
"\($gh):\($major)-\($tag_suffix)"
|
||||
else empty end
|
||||
] |
|
||||
# Floating beta tag.
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-beta" + $suffix)) then
|
||||
.tags += ["\($dh):beta\($suffix)", "\($gh):beta\($suffix)"]
|
||||
else . end |
|
||||
# Floating stable tag, plus latest for variants that have it.
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-stable" + $suffix)) then
|
||||
.tags += (
|
||||
["\($dh):stable\($suffix)", "\($gh):stable\($suffix)"]
|
||||
+ (if $addLatest then ["\($dh):latest", "\($gh):latest"] else [] end)
|
||||
)
|
||||
else . end |
|
||||
.tags | join(",")
|
||||
' "$metadata_file"
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: frontend/dist
|
||||
path: backend/internal/web/dist
|
||||
- name: Download backend build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
@@ -105,10 +105,6 @@ jobs:
|
||||
path: backend/
|
||||
- name: Make backend executable
|
||||
run: chmod +x backend/filebrowser
|
||||
- name: Copy frontend build to backend
|
||||
run: |
|
||||
mkdir -p backend/internal/web/embed
|
||||
cp -r frontend/dist/* backend/internal/web/embed
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
+6
-130
@@ -75,50 +75,8 @@ jobs:
|
||||
id: modify-json
|
||||
run: |
|
||||
JSON='${{ steps.meta.outputs.json }}'
|
||||
MODIFIED_JSON=$(echo "$JSON" | jq \
|
||||
--argjson updateTrack '${{ steps.floating.outputs.update_track == 'true' }}' \
|
||||
--argjson updateMajor '${{ steps.floating.outputs.update_major == 'true' }}' '
|
||||
.tags |= map(
|
||||
sub(":v"; ":") # Strip the 'v'
|
||||
| select(test(":latest") | not)
|
||||
) |
|
||||
# Find all version tags with suffix (X.Y.Z-suffix) and add major.minor (always) and major tags (when allowed)
|
||||
.tags += [
|
||||
.tags[]
|
||||
| select(test(":[0-9]+\\.[0-9]+\\.[0-9]+-"))
|
||||
| split(":")
|
||||
| .[-1] as $version
|
||||
| ($version | split("-")) as $version_split
|
||||
| ($version_split[0] | split(".")) as $version_parts
|
||||
| ($version_split[1:] | join("-")) as $suffix
|
||||
| ($version_parts | "\(.[0]).\(.[1])") as $major_minor
|
||||
| ($version_parts[0]) as $major
|
||||
| "gtstef/filebrowser:\($major_minor)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:\($major_minor)-\($suffix)",
|
||||
if $updateMajor then
|
||||
"gtstef/filebrowser:\($major)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:\($major)-\($suffix)"
|
||||
else empty end
|
||||
] |
|
||||
# Add suffix-only tags for beta when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-beta")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser:beta",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:beta"
|
||||
]
|
||||
else . end |
|
||||
# Add suffix-only tags for stable (and latest) when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-stable")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser:stable",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:stable",
|
||||
"gtstef/filebrowser:latest",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:latest"
|
||||
]
|
||||
else . end
|
||||
')
|
||||
TAGS_CSV=$(echo "$MODIFIED_JSON" | jq -r '.tags | join(",")')
|
||||
echo "$TAGS_CSV" > modified_tags.txt # Optional debug output
|
||||
printf '%s' "$JSON" > metadata.json
|
||||
TAGS_CSV=$(bash .github/scripts/docker-release-tags.sh metadata.json default '${{ steps.floating.outputs.update_track }}' '${{ steps.floating.outputs.update_major }}')
|
||||
echo "cleaned_tag=$TAGS_CSV" >> $GITHUB_OUTPUT
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -171,50 +129,8 @@ jobs:
|
||||
id: modify-json
|
||||
run: |
|
||||
JSON='${{ steps.meta.outputs.json }}'
|
||||
MODIFIED_JSON=$(echo "$JSON" | jq \
|
||||
--argjson updateTrack '${{ steps.floating.outputs.update_track == 'true' }}' \
|
||||
--argjson updateMajor '${{ steps.floating.outputs.update_major == 'true' }}' '
|
||||
.tags |= map(
|
||||
sub(":v"; ":") # Strip the 'v'
|
||||
| select(test(":latest") | not)
|
||||
) |
|
||||
# Find all version tags with suffix (X.Y.Z-suffix) and add major.minor (always) and major tags (when allowed)
|
||||
.tags += [
|
||||
.tags[]
|
||||
| select(test(":[0-9]+\\.[0-9]+\\.[0-9]+-"))
|
||||
| split(":")
|
||||
| .[-1] as $version
|
||||
| ($version | split("-")) as $version_split
|
||||
| ($version_split[0] | split(".")) as $version_parts
|
||||
| ($version_split[1:] | join("-")) as $suffix
|
||||
| ($version_parts | "\(.[0]).\(.[1])") as $major_minor
|
||||
| ($version_parts[0]) as $major
|
||||
| "gtstef/filebrowser-media:\($major_minor)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser-media:\($major_minor)-\($suffix)",
|
||||
if $updateMajor then
|
||||
"gtstef/filebrowser-media:\($major)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser-media:\($major)-\($suffix)"
|
||||
else empty end
|
||||
] |
|
||||
# Add suffix-only tags for beta when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-beta")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser-media:beta",
|
||||
"ghcr.io/gtsteffaniak/filebrowser-media:beta"
|
||||
]
|
||||
else . end |
|
||||
# Add suffix-only tags for stable (and latest) when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-stable")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser-media:stable",
|
||||
"ghcr.io/gtsteffaniak/filebrowser-media:stable",
|
||||
"gtstef/filebrowser-media:latest",
|
||||
"ghcr.io/gtsteffaniak/filebrowser-media:latest"
|
||||
]
|
||||
else . end
|
||||
')
|
||||
TAGS_CSV=$(echo "$MODIFIED_JSON" | jq -r '.tags | join(",")')
|
||||
echo "$TAGS_CSV" > modified_tags.txt # Optional debug output
|
||||
printf '%s' "$JSON" > metadata.json
|
||||
TAGS_CSV=$(bash .github/scripts/docker-release-tags.sh metadata.json media '${{ steps.floating.outputs.update_track }}' '${{ steps.floating.outputs.update_major }}')
|
||||
echo "cleaned_tag=$TAGS_CSV" >> $GITHUB_OUTPUT
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
@@ -267,48 +183,8 @@ jobs:
|
||||
id: modify-json-slim
|
||||
run: |
|
||||
JSON='${{ steps.meta.outputs.json }}'
|
||||
MODIFIED_JSON=$(echo "$JSON" | jq \
|
||||
--argjson updateTrack '${{ steps.floating.outputs.update_track == 'true' }}' \
|
||||
--argjson updateMajor '${{ steps.floating.outputs.update_major == 'true' }}' '
|
||||
.tags |= map(
|
||||
sub(":v"; ":") # Strip the 'v'
|
||||
| select(test(":latest") | not) # Remove the "latest" tag
|
||||
| . + "-slim" # Append "-slim" to the versioned tag
|
||||
) |
|
||||
# Find all version tags with suffix (X.Y.Z-suffix-slim) and add major.minor (always) and major tags (when allowed)
|
||||
.tags += [
|
||||
.tags[]
|
||||
| select(test(":[0-9]+\\.[0-9]+\\.[0-9]+-"))
|
||||
| split(":")
|
||||
| .[-1] as $version
|
||||
| ($version | split("-")) as $version_split
|
||||
| ($version_split[0] | split(".")) as $version_parts
|
||||
| ($version_split[1:] | join("-")) as $suffix
|
||||
| ($version_parts | "\(.[0]).\(.[1])") as $major_minor
|
||||
| ($version_parts[0]) as $major
|
||||
| "gtstef/filebrowser:\($major_minor)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:\($major_minor)-\($suffix)",
|
||||
if $updateMajor then
|
||||
"gtstef/filebrowser:\($major)-\($suffix)",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:\($major)-\($suffix)"
|
||||
else empty end
|
||||
] |
|
||||
# Add suffix-only tags for beta-slim when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-beta-slim")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser:beta-slim",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:beta-slim"
|
||||
]
|
||||
else . end |
|
||||
# Add suffix-only tags for stable-slim when semver policy allows
|
||||
if $updateTrack and any(.tags[]; split(":")[-1] | endswith("-stable-slim")) then
|
||||
.tags += [
|
||||
"gtstef/filebrowser:stable-slim",
|
||||
"ghcr.io/gtsteffaniak/filebrowser:stable-slim"
|
||||
]
|
||||
else . end
|
||||
')
|
||||
TAGS_CSV=$(echo "$MODIFIED_JSON" | jq -r '.tags | join(",")')
|
||||
printf '%s' "$JSON" > metadata.json
|
||||
TAGS_CSV=$(bash .github/scripts/docker-release-tags.sh metadata.json slim '${{ steps.floating.outputs.update_track }}' '${{ steps.floating.outputs.update_major }}')
|
||||
echo "cleaned_tag=$TAGS_CSV" >> $GITHUB_OUTPUT
|
||||
- name: Build and push slim image
|
||||
uses: docker/build-push-action@v6
|
||||
|
||||
+2
-2
@@ -43,8 +43,8 @@ frontend/.tmp-icons-*
|
||||
/backend/*test_config.yaml
|
||||
/backend/generated.yaml
|
||||
/backend/srv
|
||||
/backend/internal/web/dist
|
||||
/backend/internal/web/embed
|
||||
/backend/internal/web/dist/*
|
||||
!/backend/internal/web/dist/.gitkeep
|
||||
/backend/debug
|
||||
/backend/http
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
All notable changes to this project will be documented in this file. For commit guidelines, please refer to [Standard Version](https://github.com/conventional-changelog/standard-version).
|
||||
|
||||
## v2.2.0
|
||||
|
||||
**New Features**:
|
||||
- Core web assets are loaded into memory instead of served from disk.
|
||||
- Media enhancements
|
||||
- All server media settings can be managed in settings by admins.
|
||||
- Transcoding for media playback with quality presets -- requires ffmpeg.
|
||||
@@ -13,6 +15,17 @@ All notable changes to this project will be documented in this file. For commit
|
||||
# office previews can use embedded thumbnail if possible
|
||||
# text file previews are more efficient direct creation
|
||||
|
||||
**Notes**:
|
||||
- Smaller binaries: the Swagger UI serves only the four files it needs instead of the full swagger-ui bundle
|
||||
- UPX compression is removed from release builds.
|
||||
- Runtime debug tooling (pprof) is only compiled in with `-tags debug`. Dev targets (`make run`, `make dev`) enable it automatically.
|
||||
- The CGO SQLite driver and the `cgosql` build tag are removed.
|
||||
- A `filebrowser-media` image (`_docker/Dockerfile.media`) is published alongside the main and slim images.
|
||||
- Non-dev builds (including Docker) load the frontend into memory at startup when preload succeeds; if preload fails, assets are served from disk. Dev mode still serves from disk.
|
||||
- Frontend build output lives only in `backend/internal/web/dist`. Release builds compile that tree into the binary with `go:embed`; Docker and other disk-only runs preload or serve the same `dist` folder at runtime.
|
||||
- Release image tags are generated by one script (`.github/scripts/docker-release-tags.sh`) for the main, slim and media images.
|
||||
- `make build-docker-media` builds the media image locally.
|
||||
|
||||
**Bug Fixes**:
|
||||
- Closing a video preview from the header bar now stops playback immediately instead of leaving audio running in the background.
|
||||
- Closing a preview no longer navigates back to the same file when a stale "previous item" was remembered from an earlier navigation.
|
||||
@@ -21,6 +34,7 @@ All notable changes to this project will be documented in this file. For commit
|
||||
- getDirectoryPreview no longer requests metadata, so folder previews skip the per-file probe and subtitle scan.
|
||||
- Media probes are cached by file identity and concurrent reads are coalesced, so opening a file no longer triggers repeated ffprobe runs.
|
||||
- Client-initiated stream cancels (seek/navigation) no longer log spurious write-error responses.
|
||||
- Gzipped `.woff` font files are no longer generated without being served; only `.js` and `.woff2` are precompressed.
|
||||
|
||||
## v2.1.4
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
COPY [ "./backend/reduce-rounded-corners.css", "./no-rounded.css" ]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# generate duplicate 1.1MB files
|
||||
RUN dd if=/dev/zero of=/app/frontend/tests/playwright-files/1.1MB.bin bs=1024 count=1126 && dd if=/dev/zero of=/app/frontend/tests/playwright-files/1.1mb.bin bs=1024 count=1126
|
||||
|
||||
@@ -6,7 +6,7 @@ COPY [ "./frontend/tests", "./tests" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
RUN apt update && apt install nginx -y
|
||||
RUN mv default.conf /etc/nginx/conf.d/default.conf
|
||||
RUN sed -i 's/filebrowser/localhost/g' /etc/nginx/conf.d/default.conf
|
||||
|
||||
@@ -11,7 +11,7 @@ COPY [ "./_docker/src/settings/backend/config.yaml", "./"]
|
||||
COPY [ "./_docker/src/settings/backend/database.db.old", "./"]
|
||||
COPY [ "./backend/reduce-rounded-corners.css", "./no-rounded.css"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
RUN ./filebrowser user set testuser1 --password testuser1
|
||||
|
||||
@@ -11,7 +11,7 @@ COPY [ "./frontend/tests/", "./tests" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# Run tests
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
|
||||
@@ -11,7 +11,7 @@ COPY [ "./frontend/tests/playwright-files", "./tests/playwright-files2" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# Run tests
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
|
||||
@@ -7,7 +7,7 @@ WORKDIR /app/backend/
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./_docker/src/oidc/backend/", "./"]
|
||||
COPY ["./backend/filebrowser", "./"]
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# Run tests
|
||||
WORKDIR /app/frontend
|
||||
|
||||
@@ -23,7 +23,7 @@ WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
COPY [ "./backend/reduce-rounded-corners.css", "./no-rounded.css" ]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
ENV CI=true
|
||||
|
||||
@@ -13,7 +13,7 @@ COPY [ "./frontend/tests", "./tests" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test
|
||||
@@ -6,7 +6,7 @@ COPY [ "./frontend/tests", "./tests" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
RUN apt update && apt install nginx -y
|
||||
RUN mv default.conf /etc/nginx/conf.d/default.conf
|
||||
RUN mv .htpasswd /etc/nginx/conf.d/.htpasswd
|
||||
|
||||
@@ -10,7 +10,7 @@ COPY [ "./backend/filebrowser", "./"]
|
||||
COPY [ "./_docker/src/screenshots/backend/*", "./"]
|
||||
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
RUN ./filebrowser & sleep 2 && cd ../frontend && npx playwright test --project dark-screenshots
|
||||
@@ -13,7 +13,7 @@ WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
COPY [ "./backend/reduce-rounded-corners.css", "./no-rounded.css"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# Run tests
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
|
||||
@@ -11,7 +11,7 @@ COPY [ "./frontend/tests/", "./tests" ]
|
||||
WORKDIR /app/backend/
|
||||
COPY [ "./backend/filebrowser", "./"]
|
||||
RUN mkdir -p ./internal/web/dist
|
||||
COPY [ "./backend/internal/web/embed/", "./internal/web/dist/"]
|
||||
COPY [ "./backend/internal/web/dist/", "./internal/web/dist/"]
|
||||
|
||||
# Run tests
|
||||
ENV FILEBROWSER_PLAYWRIGHT_TEST=true
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ env_files = [".air.env"]
|
||||
entrypoint = "./tmp/filebrowser"
|
||||
args_bin = ["-c", "test_config.yaml"]
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
exclude_dir = ["tmp", "vendor", "internal/web/dist", "internal/web/embed", "internal/preview/testdata", "internal/preview/thumbnails", "internal/preview/heic", "swagger/docs", "internal/preview/gofitzinclude"]
|
||||
exclude_dir = ["tmp", "vendor", "internal/web/dist", "internal/preview/testdata", "internal/preview/thumbnails", "internal/preview/heic", "swagger/docs", "internal/preview/gofitzinclude"]
|
||||
poll = true
|
||||
poll_interval = 1000
|
||||
log = "air.log"
|
||||
|
||||
+16
-11
@@ -293,18 +293,23 @@ func createConfig(configpath string, noInput bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// setupConfigYAMLCandidates lists on-disk setup templates for config.generated.yaml.
|
||||
var setupConfigYAMLCandidates = []string{
|
||||
"frontend/public/config.generated.yaml",
|
||||
"../frontend/public/config.generated.yaml",
|
||||
"internal/web/dist/config.generated.yaml",
|
||||
"../internal/web/dist/config.generated.yaml",
|
||||
"backend/internal/web/dist/config.generated.yaml",
|
||||
}
|
||||
|
||||
func readSetupConfigTemplateYAML() (string, error) {
|
||||
return settings.ReadFirstExistingFile(setupConfigYAMLCandidates)
|
||||
}
|
||||
|
||||
func generateSetupConfigYAML(config *settings.Settings) (string, error) {
|
||||
embeddedPaths := []string{
|
||||
"frontend/public/config.generated.yaml",
|
||||
"../frontend/public/config.generated.yaml",
|
||||
"internal/web/dist/config.generated.yaml",
|
||||
"../internal/web/dist/config.generated.yaml",
|
||||
}
|
||||
for _, path := range embeddedPaths {
|
||||
embeddedYaml, err := os.ReadFile(path)
|
||||
if err == nil && len(embeddedYaml) > 0 {
|
||||
return settings.GenerateConfigYamlWithEmbedded(config, true, false, false, string(embeddedYaml))
|
||||
}
|
||||
embeddedYaml, err := readSetupConfigTemplateYAML()
|
||||
if err == nil {
|
||||
return settings.GenerateConfigYamlWithEmbedded(config, true, false, false, embeddedYaml)
|
||||
}
|
||||
return settings.GenerateConfigYaml(config, true, false, false)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func writeSetupConfigYAML(t *testing.T, root, relPath, content string) {
|
||||
t.Helper()
|
||||
path := filepath.Join(root, filepath.FromSlash(relPath))
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadSetupConfigTemplateYAML_fromDist(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeSetupConfigYAML(t, root, "internal/web/dist/config.generated.yaml", "dist-content")
|
||||
t.Chdir(root)
|
||||
|
||||
got, err := readSetupConfigTemplateYAML()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != "dist-content" {
|
||||
t.Fatalf("readSetupConfigTemplateYAML() = %q, want dist-content", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadSetupConfigTemplateYAML_prefersFrontendPublic(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeSetupConfigYAML(t, root, "frontend/public/config.generated.yaml", "from-public")
|
||||
writeSetupConfigYAML(t, root, "internal/web/dist/config.generated.yaml", "from-dist")
|
||||
t.Chdir(root)
|
||||
|
||||
got, err := readSetupConfigTemplateYAML()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != "from-public" {
|
||||
t.Fatalf("readSetupConfigTemplateYAML() = %q, want from-public", got)
|
||||
}
|
||||
}
|
||||
+5
-10
@@ -92,14 +92,7 @@ func StartFilebrowser() {
|
||||
done := make(chan struct{}) // Signals server has stopped
|
||||
shutdownComplete := make(chan struct{}) // Signals shutdown process is complete
|
||||
|
||||
// Dev mode enables development features like template hot-reloading
|
||||
_, err := os.Stat("internal/web/dist")
|
||||
// In dev mode, always use filesystem assets. Otherwise, check if internal/web/dist exists
|
||||
if !settings.Env.IsDevMode {
|
||||
settings.Env.EmbeddedFs = os.IsNotExist(err)
|
||||
}
|
||||
|
||||
err = utils.SetInvalidPasswordHash()
|
||||
err := utils.SetInvalidPasswordHash()
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to set security hash: %v", err)
|
||||
}
|
||||
@@ -206,14 +199,16 @@ func rootCMD(ctx context.Context, serverConfig *settings.Server, a *app.App, shu
|
||||
numWorkers := settings.Config.Server.NumImageProcessors
|
||||
|
||||
// Initialize asset filesystem before starting services
|
||||
if settings.Env.EmbeddedFs {
|
||||
if !settings.Env.IsDevMode && web.EmbeddedFrontendAvailable() {
|
||||
embeddedAssets := web.GetEmbeddedAssets()
|
||||
subAssets, err := fs.Sub(embeddedAssets, "embed")
|
||||
subAssets, err := fs.Sub(embeddedAssets, "dist")
|
||||
if err != nil {
|
||||
logger.Fatalf("Failed to create sub filesystem: %v", err)
|
||||
}
|
||||
settings.Env.EmbeddedFs = true
|
||||
fileutils.InitAssetFS(subAssets, true, false)
|
||||
} else {
|
||||
settings.Env.EmbeddedFs = false
|
||||
fileutils.InitAssetFS(nil, false, settings.Env.IsDevMode)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package fileutils
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
"github.com/gtsteffaniak/go-logger/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,7 +30,17 @@ func InitAssetFS(embeddedAssets fs.FS, useEmbedded bool, devMode bool) {
|
||||
// Dev mode: serve frontend source assets (templates, fonts, icons)
|
||||
assetFs = dirFS{Dir: http.Dir("../frontend/public")}
|
||||
} else {
|
||||
assetFs = dirFS{Dir: http.Dir("internal/web/dist")}
|
||||
// Non-dev disk build (e.g. Docker): dist is shipped beside the binary. Preload it
|
||||
// into memory when possible; otherwise serve from disk.
|
||||
diskAssets := dirFS{Dir: http.Dir("internal/web/dist")}
|
||||
cached, err := loadMemFS(diskAssets)
|
||||
if err != nil {
|
||||
logger.Warningf("could not preload frontend assets into memory, serving from disk: %v", err)
|
||||
assetFs = diskAssets
|
||||
} else {
|
||||
logger.Debugf("preloaded %d frontend assets (%d bytes) into memory", cached.Len(), cached.Size())
|
||||
assetFs = cached
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package fileutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/fs"
|
||||
"time"
|
||||
)
|
||||
|
||||
// memFS is a read-only fs.FS that holds every file's contents in memory.
|
||||
// It is built once at startup from a disk-backed filesystem so that asset
|
||||
// requests never touch the disk.
|
||||
type memFS struct {
|
||||
files map[string]memFile
|
||||
}
|
||||
|
||||
type memFile struct {
|
||||
data []byte
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
// loadMemFS reads every regular file under src into memory.
|
||||
func loadMemFS(src fs.FS) (*memFS, error) {
|
||||
m := &memFS{files: make(map[string]memFile)}
|
||||
err := fs.WalkDir(src, ".", func(name string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
data, err := fs.ReadFile(src, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := d.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.files[name] = memFile{data: data, modTime: info.ModTime()}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Size returns the total number of bytes held in memory.
|
||||
func (m *memFS) Size() int {
|
||||
total := 0
|
||||
for _, f := range m.files {
|
||||
total += len(f.data)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// Len returns the number of files held in memory.
|
||||
func (m *memFS) Len() int {
|
||||
return len(m.files)
|
||||
}
|
||||
|
||||
// Open implements fs.FS. Only regular files are supported, which is all the
|
||||
// asset callers need (ReadFile, Stat and template parsing).
|
||||
func (m *memFS) Open(name string) (fs.File, error) {
|
||||
if !fs.ValidPath(name) {
|
||||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrInvalid}
|
||||
}
|
||||
f, ok := m.files[name]
|
||||
if !ok {
|
||||
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
|
||||
}
|
||||
return &memOpenFile{
|
||||
Reader: bytes.NewReader(f.data),
|
||||
info: memFileInfo{
|
||||
name: baseName(name),
|
||||
size: int64(len(f.data)),
|
||||
modTime: f.modTime,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type memOpenFile struct {
|
||||
*bytes.Reader
|
||||
info memFileInfo
|
||||
}
|
||||
|
||||
func (f *memOpenFile) Stat() (fs.FileInfo, error) { return f.info, nil }
|
||||
func (f *memOpenFile) Close() error { return nil }
|
||||
|
||||
type memFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (i memFileInfo) Name() string { return i.name }
|
||||
func (i memFileInfo) Size() int64 { return i.size }
|
||||
func (i memFileInfo) Mode() fs.FileMode { return 0o444 }
|
||||
func (i memFileInfo) ModTime() time.Time { return i.modTime }
|
||||
func (i memFileInfo) IsDir() bool { return false }
|
||||
func (i memFileInfo) Sys() any { return nil }
|
||||
|
||||
func baseName(name string) string {
|
||||
for i := len(name) - 1; i >= 0; i-- {
|
||||
if name[i] == '/' {
|
||||
return name[i+1:]
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package fileutils
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadMemFSServesFromMemory(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
files := map[string]string{
|
||||
"index.html": "<html>index</html>",
|
||||
"assets/app.js.gz": "gzip-bytes",
|
||||
"fonts/icons.woff2": "font-bytes",
|
||||
}
|
||||
for name, content := range files {
|
||||
path := filepath.Join(dir, filepath.FromSlash(name))
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
m, err := loadMemFS(dirFS{Dir: http.Dir(dir)})
|
||||
if err != nil {
|
||||
t.Fatalf("loadMemFS: %v", err)
|
||||
}
|
||||
if m.Len() != len(files) {
|
||||
t.Fatalf("loaded %d files, want %d", m.Len(), len(files))
|
||||
}
|
||||
|
||||
// Remove everything from disk: the cache must still serve every file.
|
||||
if removeErr := os.RemoveAll(dir); removeErr != nil {
|
||||
t.Fatal(removeErr)
|
||||
}
|
||||
for name, want := range files {
|
||||
got, readErr := fs.ReadFile(m, name)
|
||||
if readErr != nil {
|
||||
t.Fatalf("ReadFile(%s) after disk removal: %v", name, readErr)
|
||||
}
|
||||
if string(got) != want {
|
||||
t.Errorf("ReadFile(%s) = %q, want %q", name, got, want)
|
||||
}
|
||||
info, statErr := fs.Stat(m, name)
|
||||
if statErr != nil {
|
||||
t.Fatalf("Stat(%s): %v", name, statErr)
|
||||
}
|
||||
if info.IsDir() || info.Size() != int64(len(want)) || info.Name() != filepath.Base(name) {
|
||||
t.Errorf("Stat(%s) = %+v, unexpected", name, info)
|
||||
}
|
||||
}
|
||||
|
||||
// Template-style glob on a plain name must resolve via Stat.
|
||||
matches, globErr := fs.Glob(m, "index.html")
|
||||
if globErr != nil || len(matches) != 1 {
|
||||
t.Fatalf("Glob(index.html) = %v, %v", matches, globErr)
|
||||
}
|
||||
|
||||
if _, readErr := fs.ReadFile(m, "missing.css"); readErr == nil {
|
||||
t.Error("ReadFile(missing.css) should fail")
|
||||
}
|
||||
if _, readErr := fs.ReadFile(m, "../escape"); readErr == nil {
|
||||
t.Error("invalid path should be rejected")
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package gomedia
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSupportsVideoContainer(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"movie.mp4": true,
|
||||
"MOVIE.MKV": true,
|
||||
"clip.webm": true,
|
||||
"clip.mov": true,
|
||||
"clip.m4v": true,
|
||||
"old.avi": false,
|
||||
"legacy.wmv": false,
|
||||
"stream.ts": false,
|
||||
"no-extension": false,
|
||||
"dir.mp4/folder": false,
|
||||
}
|
||||
for path, want := range cases {
|
||||
require.Equal(t, want, supportsVideoContainer(path), path)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNonTargetVideoContainerSkipsGoMedia checks that containers go-media does
|
||||
// not target return the fallback result without reading the file. The path does
|
||||
// not exist, so any file read would also fail. Only the extension gate can
|
||||
// return false here before go-media is called.
|
||||
func TestNonTargetVideoContainerSkipsGoMedia(t *testing.T) {
|
||||
withGate(t, false)
|
||||
missing := filepath.Join(t.TempDir(), "missing.wmv")
|
||||
|
||||
_, ok := Probe(missing, nil)
|
||||
require.False(t, ok)
|
||||
_, ok = VideoThumbnail(missing, 256, 256, 72, 10, nil)
|
||||
require.False(t, ok)
|
||||
_, ok = EmbeddedSubtitles(missing, time.Now(), nil)
|
||||
require.False(t, ok)
|
||||
_, ok = ExtractSubtitle(missing, 0, nil)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
// encoderAvailable reports whether ffmpeg was built with the named encoder.
|
||||
func encoderAvailable(t *testing.T, name string) bool {
|
||||
t.Helper()
|
||||
requireTool(t, "ffmpeg")
|
||||
out, err := exec.Command("ffmpeg", "-hide_banner", "-encoders").Output()
|
||||
require.NoError(t, err)
|
||||
return bytes.Contains(out, []byte(" "+name+" "))
|
||||
}
|
||||
|
||||
// makeMKV encodes a short clip with the given video encoder into an MKV file.
|
||||
func makeMKV(t *testing.T, encoder string) string {
|
||||
t.Helper()
|
||||
path := filepath.Join(t.TempDir(), "clip.mkv")
|
||||
out, err := exec.Command("ffmpeg", "-v", "error", "-y",
|
||||
"-f", "lavfi", "-i", "testsrc2=size=320x240:rate=25",
|
||||
"-t", "2", "-c:v", encoder, "-b:v", "500k", "-an", path).CombinedOutput()
|
||||
require.NoError(t, err, string(out))
|
||||
return path
|
||||
}
|
||||
|
||||
// TestH264MatroskaThumbnailFallsBack checks that H.264 in MKV, which go-media
|
||||
// does not decode, is reported as unsupported so the caller uses ffmpeg.
|
||||
func TestH264MatroskaThumbnailFallsBack(t *testing.T) {
|
||||
withGate(t, false)
|
||||
if !encoderAvailable(t, "libx264") {
|
||||
t.Skip("ffmpeg has no libx264 encoder")
|
||||
}
|
||||
mkv := makeMKV(t, "libx264")
|
||||
|
||||
_, ok := VideoThumbnail(mkv, 160, 120, 72, 10, nil)
|
||||
require.False(t, ok, "go-media must not thumbnail H.264 in MKV")
|
||||
}
|
||||
|
||||
// TestVP8MatroskaThumbnailUsesGoMedia checks that the codec gate in go-media
|
||||
// does not block VP8, which go-media still decodes.
|
||||
func TestVP8MatroskaThumbnailUsesGoMedia(t *testing.T) {
|
||||
withGate(t, false)
|
||||
if !encoderAvailable(t, "libvpx") {
|
||||
t.Skip("ffmpeg has no libvpx encoder")
|
||||
}
|
||||
mkv := makeMKV(t, "libvpx")
|
||||
|
||||
data, ok := VideoThumbnail(mkv, 160, 120, 72, 50, nil)
|
||||
require.True(t, ok, "go-media must thumbnail VP8 in MKV")
|
||||
require.True(t, strings.HasPrefix(string(data[:2]), "\xff\xd8"), "output must be JPEG")
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1 @@
|
||||
.activity-details-table[data-v-be79e288] thead th,.activity-details-table[data-v-be79e288] tbody td{padding:.5em}.activity-details-table[data-v-be79e288] tbody td:first-child{color:var(--textSecondary);white-space:nowrap;vertical-align:top;font-weight:500}.activity-details-table[data-v-be79e288] tbody td:last-child{overflow-wrap:break-word;white-space:pre-wrap}.activity-table-value-info[data-v-d0f8eda4] thead th,.activity-table-value-info[data-v-d0f8eda4] tbody td{padding:.5em}.activity-table-value-info[data-v-d0f8eda4] tbody td{overflow-wrap:break-word;white-space:pre-wrap}.share-picker-field[data-v-97464281]{align-items:stretch;gap:.35rem;max-width:100%;display:flex}.unified-share-picker[data-v-97464281]{overflow-wrap:break-word;box-sizing:border-box;flex:1;justify-content:flex-start;align-items:center;min-width:0;display:flex}.share-clear[data-v-97464281]{min-width:unset;flex-shrink:0;padding:.35rem}.share-clear .material-symbols[data-v-97464281]{font-size:1.1rem}.activity-viewer[data-v-2d793054]{max-width:1200px;margin-left:auto;margin-right:auto;padding:1em}.activity-scope-card[data-v-2d793054]{margin-bottom:1rem}.activity-scope-select[data-v-2d793054]{width:100%;padding:.65rem .85rem;font-size:1rem}.path-filter-mode[data-v-2d793054]{flex-wrap:wrap;gap:1rem;margin-bottom:.75rem;display:flex}.mode-option[data-v-2d793054]{cursor:pointer;align-items:center;gap:.35rem;font-size:.9rem;display:flex}.path-filter-picker[data-v-2d793054]{margin-top:.75rem}.path-filter-picker[data-v-2d793054] .unified-path-picker{max-width:100%}.path-filter-glob[data-v-2d793054]{margin-top:.75rem}.glob-fields[data-v-2d793054]{flex-wrap:wrap;gap:.75rem;display:flex}.glob-field[data-v-2d793054]{flex:1;min-width:10rem}.glob-field-wide[data-v-2d793054]{flex:2;min-width:14rem}.share-hash-field[data-v-2d793054]{margin-top:.75rem}.activity-viewer-results[data-v-2d793054]{margin-top:1.25rem;margin-bottom:2em}.results-table[data-v-2d793054]{min-width:0}.results-table-scroll[data-v-2d793054]{max-width:100%}.results-table-scroll[data-v-2d793054] .settings-table{table-layout:fixed;width:100%}.results-table-scroll[data-v-2d793054] .settings-table tbody td{max-width:0;overflow:hidden}@media (width<=768px){.results-table-scroll[data-v-2d793054]{width:100%;max-width:1000px;overflow-x:auto}.results-table-scroll[data-v-2d793054] .settings-table{table-layout:auto;width:max-content;min-width:100%;max-width:1000px}.results-table-scroll[data-v-2d793054] .settings-table thead th,.results-table-scroll[data-v-2d793054] .settings-table tbody td{white-space:nowrap;max-width:none}}.results-table-scroll[data-v-2d793054] .table-value-cell{min-width:0;max-width:100%;display:block;overflow:hidden}.results-table-scroll[data-v-2d793054] .table-value-badge,.results-table-scroll[data-v-2d793054] .detail-badge{box-sizing:border-box;vertical-align:top;border:1px solid var(--divider);white-space:nowrap;text-overflow:ellipsis;min-width:0;max-width:100%;color:var(--textPrimary,inherit);background:0 0;padding:.12rem .5rem;font-size:.8em;display:inline-block;overflow:hidden}.results-table-scroll[data-v-2d793054] .details-muted{color:var(--textSecondary);font-size:.9em}.results-stats[data-v-2d793054]{color:var(--textSecondary);flex-wrap:wrap;align-items:baseline;gap:.75rem 2rem;margin-bottom:.75rem;font-size:.95rem;display:flex}.results-chart[data-v-2d793054]{margin-top:.25rem}.chart-interval-note[data-v-2d793054]{color:var(--textSecondary);margin:0 0 .75rem;font-size:.9rem}.results-empty[data-v-2d793054]{text-align:center;padding:3rem 1.5rem 4rem}.results-empty-hint[data-v-2d793054]{color:var(--textSecondary);margin:0;font-size:.95rem}.results-empty .lonely-message[data-v-2d793054]{margin-bottom:.5rem}.config-grid[data-v-2d793054]{grid-template-columns:repeat(auto-fill,minmax(12rem,1fr));align-items:end;gap:1rem;display:grid}.config-grid.mobile[data-v-2d793054]{grid-template-columns:1fr}.config-field[data-v-2d793054]{min-width:0}.config-field h3[data-v-2d793054]{text-align:center;margin:0 0 .5rem;font-size:.95rem;font-weight:600}.config-field-wide[data-v-2d793054]{grid-column:1/-1}.custom-range[data-v-2d793054]{flex-wrap:wrap;grid-column:1/-1;gap:.5rem;display:flex}.custom-range .input[data-v-2d793054]{flex:1;min-width:10rem}.config-actions[data-v-2d793054]{flex-wrap:wrap;grid-column:1/-1;align-items:center;gap:.5rem;margin-top:.5rem;display:flex}.button[data-v-2d793054]{align-items:center;gap:.5rem;display:flex}.button .material-symbols[data-v-2d793054]{font-size:1.2rem}.pagination[data-v-2d793054]{justify-content:center;align-items:center;gap:1rem;margin-top:1rem;display:flex}.page-label[data-v-2d793054]{opacity:.85}.details-cell-wrap[data-v-2d793054]{min-width:0;max-width:100%;overflow:hidden}.details-badges[data-v-2d793054]{flex-wrap:nowrap;gap:.35rem;min-width:0;max-width:100%;display:flex;overflow:hidden}.results-table-scroll[data-v-2d793054] .details-badges .detail-badge{vertical-align:top;flex:1 1 0;min-width:0;max-width:100%;display:inline-block}.details-restriLine truncated
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.settings-number-input[data-v-f3e670fc],.settings-number-input>div[data-v-f3e670fc]{align-items:center;gap:1em;display:flex}.settings-number-input[data-v-f3e670fc]{flex-wrap:wrap;justify-content:space-between}.range-value[data-v-f3e670fc]{text-align:center;min-width:4ch}.advanced-search-root[data-v-f3e670fc]{flex-direction:column;flex:1;align-self:stretch;width:100%;min-height:0;display:flex}.advanced-search-toolbar[data-v-f3e670fc]{margin:1em}.advanced-search-card-form[data-v-f3e670fc]{box-sizing:border-box;flex-direction:column;width:100%;display:flex}.advanced-search-config-row[data-v-f3e670fc]{box-sizing:border-box;flex-wrap:wrap;align-items:flex-start;gap:1.25rem;width:100%;display:flex}.config-primary[data-v-f3e670fc],.config-secondary[data-v-f3e670fc]{box-sizing:border-box;flex:min(500px,100%);min-width:min(500px,100%);max-width:100%}.config-secondary[data-v-f3e670fc]{flex-direction:column;gap:1rem;display:flex}.advanced-search-options-section[data-v-f3e670fc]{box-sizing:border-box;width:100%}.advanced-search-options-section[data-v-f3e670fc] .settings-group{width:100%;max-width:100%}.as-section-label[data-v-f3e670fc]{color:var(--textSecondary);text-transform:uppercase;letter-spacing:.02em;margin-bottom:.35rem;font-size:.8125rem;font-weight:600}.term-join-divider[data-v-f3e670fc]{box-sizing:border-box;justify-content:center;align-items:center;gap:.2em;width:fit-content;max-width:100%;margin:.25em auto;display:flex}.term-join-line[data-v-f3e670fc]{background-color:var(--divider,#aaa);flex:none;align-self:center;width:1.65em;height:1px}.term-join-text[data-v-f3e670fc]{color:var(--textSecondary);-webkit-user-select:none;user-select:none;flex-shrink:0;font-size:.8125rem;font-weight:600;line-height:1.2}.advanced-search-inline-header[data-v-f3e670fc]{flex-shrink:0;width:100%}.advanced-search-sticky-header[data-v-f3e670fc]{z-index:999;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);padding:.5em 0;transition:transform .25s,opacity .25s;position:sticky;top:0}.advanced-search-sticky-header.header-hidden[data-v-f3e670fc]{opacity:0;pointer-events:none;transform:translateY(-100%)}.search-term-row[data-v-f3e670fc]{align-items:stretch;gap:0;display:flex}.flex-grow-input[data-v-f3e670fc]{flex:1;min-width:0}.add-term-button[data-v-f3e670fc]{margin:.35rem 0 .75rem}.advanced-search-switch[data-v-f3e670fc]{margin-top:.25rem}.source-dropdown-select[data-v-f3e670fc]{width:100%}.scope-picker[data-v-f3e670fc]{margin-top:.35rem}.advanced-search-actions-row[data-v-f3e670fc]{flex-wrap:wrap;align-items:center;gap:.75rem;width:100%;margin-top:auto;display:flex}.search-submit[data-v-f3e670fc]{margin-left:auto}@media (width<=559px){.advanced-search-actions-row .search-submit[data-v-f3e670fc]{flex:100%;width:100%;margin-left:0}}.advanced-search-results-plane[data-v-f3e670fc]{flex-direction:column;flex:1;width:100%;min-height:min(65vh,800px);display:flex}.results-loading[data-v-f3e670fc]{justify-content:center;align-items:center;gap:.75em;padding:2rem;display:flex}.padded-message[data-v-f3e670fc]{padding:1.5rem}.error-message[data-v-f3e670fc]{color:var(--error,#c00)}.empty-state-message[data-v-f3e670fc]{text-align:center;color:var(--textSecondary)}.spin[data-v-f3e670fc]{animation:1s linear infinite rotate-f3e670fc}@keyframes rotate-f3e670fc{to{transform:rotate(360deg)}}.advanced-search-listing-results .listing-items[data-v-f3e670fc]{position:relative}.advanced-search-listing-results[data-v-f3e670fc]{margin:0 1em 1em}.advanced-search-inner[data-v-f3e670fc]{margin-top:.5em}.advanced-search-options-section[data-v-f3e670fc] .constraints{box-sizing:border-box;width:calc(100% - 2em);margin-left:1em;margin-right:1em}
|
||||
@@ -0,0 +1 @@
|
||||
.viewer-background[data-v-47d3581c]{box-sizing:border-box;background-color:#f0f2f5;width:100%;height:100%;padding:2em;overflow:hidden auto}.docx-page[data-v-47d3581c]{box-sizing:border-box;color:#000;overflow-wrap:anywhere;background:#fff;width:min(8.5in,100%);min-height:11in;margin:0 auto;padding:clamp(1em,8vw,1in);box-shadow:0 0 10px #00000026}.docx-page[data-v-47d3581c] img,.docx-page[data-v-47d3581c] svg,.docx-page[data-v-47d3581c] video{max-width:100%;height:auto}.docx-page[data-v-47d3581c] table{border-collapse:collapse;max-width:100%;margin:1em 0;display:block;overflow-x:auto}.docx-page[data-v-47d3581c] td,.docx-page[data-v-47d3581c] th{vertical-align:top;text-align:left;border:1px solid #000;padding:.4em .7em}.docx-page[data-v-47d3581c] th{background-color:#f3f4f6;font-weight:600}.docx-page[data-v-47d3581c] td>p,.docx-page[data-v-47d3581c] th>p{margin:0}.docx-page[data-v-47d3581c] pre{max-width:100%;overflow-x:auto}.status-text[data-v-47d3581c]{text-align:center;color:#333;padding:3em;font-family:sans-serif;font-size:1.2em}.status-text.error[data-v-47d3581c]{color:var(--red)}@media (width<=8.5in){.viewer-background[data-v-47d3581c]{padding:0}.docx-page[data-v-47d3581c]{min-height:100%;box-shadow:none}}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.duplicate-finder[data-v-83e23830]{max-width:1200px;margin-left:auto;margin-right:auto;padding:1em}.duplicate-finder-results[data-v-83e23830]{margin-bottom:2em}.button[data-v-83e23830]{align-items:center;gap:.5rem;margin-top:1rem;display:flex}.button .material-symbols[data-v-83e23830]{font-size:1.2rem}.error-message[data-v-83e23830]{color:var(--dark-red);border:1px solid color-mix(in srgb, var(--red), transparent 60%);background:#fee;border-radius:4px;margin-bottom:1rem;padding:1rem}.hint[data-v-83e23830]{color:var(--textSecondary);margin-top:.25rem;margin-bottom:1rem;font-size:.85rem}.stats[data-v-83e23830]{background:var(--surfaceSecondary);border-radius:4px;gap:2rem;margin-bottom:1.5rem;padding:1rem;display:flex}.duplicate-groups[data-v-83e23830]{flex-direction:column;gap:1.5rem;display:flex}.duplicate-group[data-v-83e23830]{border:1px solid var(--divider);border-radius:var(--borderRadius);overflow:hidden}.group-header[data-v-83e23830]{background:var(--surfaceSecondary);border-radius:.5em;align-items:center;gap:1rem;padding:1rem;display:flex}.group-title[data-v-83e23830]{font-size:1rem;font-weight:600}.group-size[data-v-83e23830]{color:var(--textSecondary);font-size:.9rem}.wasted-space[data-v-83e23830]{color:var(--icon-red);margin-left:auto;font-size:.9rem;font-weight:600}.group-files[data-v-83e23830]{flex-direction:column;padding:0;display:flex}.file-item-wrapper[data-v-83e23830]{border:1px solid #0000001a;border-top:0;border-radius:0;align-items:center;gap:.5rem;padding:.5em;display:flex}.file-item-wrapper[data-v-83e23830]:first-child{border-top:1px solid #0000001a}.file-item-wrapper.deleted[data-v-83e23830]{opacity:.5;background:var(--surfaceSecondary)}.file-item-wrapper.failed[data-v-83e23830]{border-left:3px solid var(--icon-red)}.file-item-content[data-v-83e23830]{flex-direction:column;flex:1;gap:.25rem;display:flex}.group-files .listing-item[data-v-83e23830]{cursor:pointer;width:100%;margin:.25em}.group-files .listing-item.activebutton[data-v-83e23830]{background:var(--primaryColor)!important;color:#fff!important}.path-text[data-v-83e23830]{white-space:nowrap;text-overflow:ellipsis;overflow:hidden;color:var(--textSecondary)!important;font-size:.75rem!important}.empty-state[data-v-83e23830]{text-align:center;color:var(--textSecondary);padding:4rem 2rem}.empty-state .material-symbols[data-v-83e23830]{opacity:.3;margin-bottom:1rem;font-size:4rem}.empty-state p[data-v-83e23830]{font-size:1.1rem}.spin[data-v-83e23830]{animation:1s linear infinite spin-83e23830}@keyframes spin-83e23830{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (width<=768px){.duplicate-finder[data-v-83e23830]{padding:1rem}.stats[data-v-83e23830]{flex-direction:column;gap:.5rem}.group-header[data-v-83e23830]{flex-wrap:wrap}.wasted-space[data-v-83e23830]{width:100%;margin-left:0}}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.editor-toolbar[data-v-83c0efe7]{background:var(--background);border-bottom:1px solid var(--alt-background);scrollbar-width:none;flex-shrink:0;align-items:center;gap:.15em;padding:.35em 0;display:flex;overflow:auto hidden}.editor-toolbar-btn[data-v-83c0efe7]{border-radius:var(--borderRadius);width:2em;height:2em;color:var(--textPrimary);cursor:pointer;background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;transition:opacity .15s;display:flex;position:relative}.md-toolbar-group[data-v-83c0efe7]{flex-shrink:0;display:flex}.editor-toolbar-sticky[data-v-83c0efe7]{z-index:1;background:var(--background);border-right:1px solid var(--alt-background);flex-shrink:0;align-items:center;margin:-.35em 0;padding:.25em;display:flex;position:sticky;left:0}.editor-toolbar-sticky--right[data-v-83c0efe7]{border-left:1px solid var(--alt-background);border-right:none;padding-right:calc(.25em + 1px);right:-1px}.toolbar-color-indicator[data-v-83c0efe7]{background-color:var(--alt-background);width:1.4em;height:.5em;box-shadow:inset 0 0 0 1px var(--alt-background);cursor:pointer;border-radius:.2em;position:absolute;bottom:.15em;left:50%;transform:translate(-50%)}.editor-toolbar-btn .material-symbols.toolbar-color-glyph[data-v-83c0efe7]{font-size:1em;transform:translateY(-.2em)}.toolbar-color-indicator--empty[data-v-83c0efe7]{opacity:0;background-color:#0000}.color-input[data-v-83c0efe7]{opacity:0;cursor:pointer;border:none;width:100%;height:100%;margin:0;padding:0;position:absolute;inset:0}.editor-toolbar-btn[data-v-83c0efe7]:disabled{opacity:.35}.editor-toolbar-btn .material-symbols[data-v-83c0efe7]{font-size:1.2em}.toolbar-icon-enter-active[data-v-83c0efe7],.toolbar-icon-leave-active[data-v-83c0efe7]{transition:opacity .12s,transform .12s}.toolbar-icon-enter-from[data-v-83c0efe7],.toolbar-icon-leave-to[data-v-83c0efe7]{opacity:0;transform:scale(.6)}.editor-toolbar-menu[data-v-83c0efe7]{z-index:9999;margin:0;padding:.25em .25em .65em;list-style:none;position:fixed}.editor-toolbar-menu--icon-menu[data-v-83c0efe7]{gap:.15em;padding-bottom:.5em;display:flex}.editor-toolbar-menu-btn[data-v-83c0efe7]{justify-content:flex-start;gap:.5em;width:100%;height:auto;padding:.5em .75em}.editor-toolbar-menu-btn .material-symbols[data-v-83c0efe7]{font-size:1.1em}.expand-enter-active[data-v-83c0efe7],.expand-leave-active[data-v-83c0efe7]{overflow:hidden}.split-divider[data-v-351bb28e]{cursor:col-resize;background-color:#0000;flex:0 0 5px;position:relative}.split-divider[data-v-351bb28e]:before{content:"";background-color:var(--alt-background);width:1px;position:absolute;top:0;bottom:0;left:2px}.split-divider[data-v-351bb28e]:hover:before,.split-divider.resizing[data-v-351bb28e]:before{background-color:var(--primaryColor)}.split-preview-pane[data-v-351bb28e]{flex:none;min-width:0;height:100%;position:relative;overflow:hidden}.split-preview-scrollbar[data-v-351bb28e]{scrollbar-width:none;width:100%;height:100%;overflow-y:auto}.split-preview-scrollbar[data-v-351bb28e]::-webkit-scrollbar{width:0;display:none}#editor-root[data-v-e20ff5ce]{height:100%}#editor-root.split-active[data-v-e20ff5ce]{width:100%;height:100%;display:flex}#editor-container[data-v-e20ff5ce]{flex-direction:column;width:100%;height:100%;display:flex;position:relative;overflow:hidden}#editor-root.split-active #editor-container[data-v-e20ff5ce]{flex:none;min-width:0;position:relative}#editor-container #editor[data-v-e20ff5ce]{flex:1;min-height:0}#editor-container.viewer-mode[data-v-e20ff5ce]{position:absolute;inset:0}.ace_editor{--ace-dark-bg:#151515;--ace-popup-bg:#1a1a1a;--ace-control-bg:#232323;font-size:14px;line-height:1.4;z-index:1!important}.ace_content{-webkit-user-select:text;user-select:text}.ace_editor .ace_selection{background-color:color-mix(in srgb, var(--primaryColor) 25%, transparent)!important}.ace_editor .ace_selection.ace_start{box-shadow:0 0 3px 0 color-mix(in srgb, var(--primaryColor) 40%, transparent)!important}.ace_editor .ace_gutter-active-line{background-color:color-mix(in srgb, var(--primaryColor) 20%, transparent)!important;color:var(--primaryColor)!important;font-weight:700!important}.ace_editor .ace_indent-guide{border-right:1px solid color-mix(in srgb, var(--primaryColor) 50%, transparent)!important;opacity:1!important;z-index:5!important}.ace_editor .ace_indent-guide-active{border-right:1px solid color-mix(in srgb, var(--primaryColor) 75%, transparent)!important}.ace_editor.ace_dark{background-color:var(--ace-dark-bg)!important}.ace_editor.ace_dark .ace_marker-layer .ace_active-line{background:color-mix(in srgb, var(--surfaceSecondary) 35%, transparent)}#editor-root.split-active .ace_scrollbar{scrollbar-width:none}#editor-root.split-active .ace_scrollbar::-webkit-scrollbar{display:none}.ace_editor .ace_search{color:var(--textPrimary);border-color:var(--divider)}.ace_editor.ace_dark .ace_search{background-color:var(--ace-popup-bg)}.ace_editor .ace_search_field{color:var(--textPrimary);border:1px solid var(--divider)}.ace_editorLine truncated
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.epub-container[data-v-2f4b46b5]{background-color:var(--background);z-index:1000;width:100%;height:100%;position:relative}.loading-indicator[data-v-2f4b46b5]{height:100%;color:var(--textSecondary);justify-content:center;align-items:center;font-size:1.2em;display:flex}#viewer[data-v-2f4b46b5]{visibility:hidden;width:100%;height:100%}#viewer.ready[data-v-2f4b46b5]{visibility:visible}.navigation[data-v-2f4b46b5]{z-index:1001;background-color:var(--surfacePrimary);border-radius:var(--borderRadius);align-items:center;gap:1em;padding:.5em;display:flex;position:absolute;bottom:1em;left:50%;transform:translate(-50%);box-shadow:0 2px 10px #0000001a}.nav-button[data-v-2f4b46b5]{border-radius:var(--borderRadius);color:var(--textPrimary);cursor:pointer;background-color:#0000;padding:.25em 1em;font-size:1.5em;transition:background-color .2s}.nav-button[data-v-2f4b46b5]:hover{background-color:var(--hoverOverlay);color:var(--primaryColor)}
|
||||
@@ -0,0 +1 @@
|
||||
.image-ex-container{overscroll-behavior:none;touch-action:none;justify-content:center;width:100%;max-width:100%;height:100%;min-height:0;max-height:100%;display:flex;position:relative;overflow:hidden}.image-ex-img{object-fit:contain;max-width:100%;max-height:100%;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.image-loading-overlay{z-index:10;justify-content:center;align-items:center;display:flex;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.file-watcher[data-v-a9d38c6d]{max-width:1200px;margin-left:auto;margin-right:auto;padding:1em}.file-watcher-config[data-v-a9d38c6d]{margin-bottom:2em}.searchContext[data-v-a9d38c6d]{margin-bottom:0!important}.config-row[data-v-a9d38c6d]{flex-wrap:nowrap;align-items:center;gap:.75rem;min-width:0;display:flex;padding:1em!important}.config-row.mobile[data-v-a9d38c6d]{flex-direction:column;align-items:stretch}.config-row-second[data-v-a9d38c6d]{flex:none;align-items:center;gap:.75rem;display:flex}.config-row-second.mobile[data-v-a9d38c6d]{grid-template-columns:minmax(0,1fr) auto auto;align-items:center;gap:.5rem;width:100%;display:grid}.config-item[data-v-a9d38c6d]{align-items:center;display:flex}.config-item.lines-input[data-v-a9d38c6d]{flex-direction:row;align-items:center;gap:.5rem}.lines-label[data-v-a9d38c6d]{color:var(--textSecondary);white-space:nowrap;flex-shrink:0;margin:0;font-size:.875rem}.file-picker-button[data-v-a9d38c6d]{white-space:nowrap;text-overflow:ellipsis;align-items:center;gap:.5rem;width:100%;display:flex;overflow:hidden}.file-picker[data-v-a9d38c6d]{flex:auto;min-width:0}.config-item.interval-select[data-v-a9d38c6d]{min-width:0}.config-row.mobile .file-picker[data-v-a9d38c6d]{width:100%}.file-watcher-output[data-v-a9d38c6d]{width:100%;padding:0!important}.status-indicator[data-v-a9d38c6d]{color:var(--textSecondary);align-items:center;gap:.5rem;font-size:.9rem;display:flex}.terminal-header[data-v-a9d38c6d]{background:var(--surfacePrimary);border-bottom:1px solid var(--divider);border-bottom-right-radius:0;border-bottom-left-radius:0;flex-direction:column;gap:.5rem;margin-bottom:0;padding:.75rem 1rem;font-size:.9rem;display:flex}.header-row[data-v-a9d38c6d]{justify-content:space-between;align-items:center;display:flex;position:relative}.header-row-first[data-v-a9d38c6d]{min-height:1.5rem}.header-row-second[data-v-a9d38c6d]{min-height:1.5rem;padding-top:.25rem}.header-row-second-mobile[data-v-a9d38c6d]{justify-content:center;min-height:1.5rem;padding-top:.25rem}.header-row-third-mobile[data-v-a9d38c6d]{min-height:1.5rem;padding-top:.25rem}.header-center-mobile[data-v-a9d38c6d]{justify-content:center;align-items:center;gap:.5rem;width:100%;display:flex}.header-left[data-v-a9d38c6d],.header-center[data-v-a9d38c6d],.header-right[data-v-a9d38c6d]{align-items:center;gap:.5rem;display:flex}.header-center[data-v-a9d38c6d]{white-space:nowrap;position:absolute;left:50%;transform:translate(-50%)}.header-right[data-v-a9d38c6d]{justify-content:flex-end;margin-left:auto}.header-filename[data-v-a9d38c6d]{color:var(--textPrimary);font-weight:700}.header-filesize[data-v-a9d38c6d]{color:var(--textSecondary)}.header-label[data-v-a9d38c6d]{color:var(--textSecondary);font-weight:500}.header-value[data-v-a9d38c6d]{color:var(--textPrimary);font-weight:600}.header-value.latency-good[data-v-a9d38c6d]{color:var(--icon-green)}.header-value.latency-ok[data-v-a9d38c6d]{color:var(--icon-deep-orange)}.header-value.latency-slow[data-v-a9d38c6d]{color:var(--icon-red)}.header-value.latency-inactive[data-v-a9d38c6d]{color:var(--textSecondary)}.status-dot[data-v-a9d38c6d]{background:var(--icon-green);border-radius:50%;width:8px;height:8px;animation:2s infinite pulse-a9d38c6d}@keyframes pulse-a9d38c6d{0%,to{opacity:1}50%{opacity:.5}}.terminal-output[data-v-a9d38c6d]{background:var(--iconBackground);min-height:100px;color:var(--textPrimary);border-top-left-radius:0;border-top-right-radius:0;padding:1rem;font-family:Courier New,Consolas,Monaco,monospace;font-size:14px;overflow:auto}.terminal-line[data-v-a9d38c6d]{white-space:nowrap;align-items:flex-start;gap:.5rem;margin-bottom:2px;line-height:1.5;display:flex}.terminal-text[data-v-a9d38c6d]{white-space:nowrap}.empty-state[data-v-a9d38c6d]{text-align:center;color:var(--textSecondary);padding:4rem 2rem}.empty-state .material-symbols[data-v-a9d38c6d]{opacity:.3;margin-bottom:1rem;font-size:4rem}.empty-state p[data-v-a9d38c6d]{font-size:1.1rem}@media (width<=768px){.file-watcher[data-v-a9d38c6d]{padding:1rem}.config-row[data-v-a9d38c6d]{flex-direction:column;align-items:stretch;gap:.5rem}.config-item.file-picker[data-v-a9d38c6d]{width:100%!important}.config-item.file-picker .file-picker-button[data-v-a9d38c6d]{width:100%}.config-row-second[data-v-a9d38c6d]{grid-template-columns:minmax(0,1fr) auto auto;gap:.5rem;width:100%;display:grid}.config-item.interval-select[data-v-a9d38c6d],.config-item.lines-input[data-v-a9d38c6d],.config-item.play-button[data-v-a9d38c6d]{flex:1;align-items:center;width:auto!important}.terminal-output[data-v-a9d38c6d]{min-height:100px;font-size:12px}}@media (width<=1100px){.config-row[data-v-a9d38c6d]:not(.mobile){flex-direction:column;align-items:stretch;gap:.5rem}.config-row:not(.mobile) .config-row-second[data-v-a9d38c6d]{grid-template-columns:minmax(0,1fr) auto auto;gap:.5rem;width:100%;display:grid}}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
#markedown-viewer{overflow-wrap:break-word;margin:1em}#markedown-viewer.html-viewer-mode{box-sizing:border-box;flex-direction:column;width:calc(100% - 1em);margin:.5em;display:flex;overflow:hidden}#markedown-viewer .markdown-content-container{background-color:color-mix(in srgb, var(--surfacePrimary) 75%, transparent);border-radius:var(--borderRadius);box-shadow:var(--surfaceElevationShadow);margin:0 auto;padding:1.2em}#markedown-viewer .markdown-content,#markedown-viewer .html-content{width:100%}#markedown-viewer .html-content{box-sizing:border-box;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;background:#fff;border:none;min-height:24em}@media (prefers-color-scheme:dark){#markedown-viewer .html-content{--lightningcss-light: ;--lightningcss-dark:initial}}#markedown-viewer.html-viewer-mode .html-content{flex:auto;width:100%;height:100%;min-height:0}#markedown-viewer .html-content img{max-width:100%;height:auto}#markedown-viewer .spacer{pointer-events:none;width:100%}#markedown-viewer .markdown-content-container .code-block-wrapper{background-color:var(--background);border-radius:.5em;max-width:100%;margin:1em 0;font-family:SFMono-Regular,Monaco,Inconsolata,Liberation Mono,Courier New,monospace;font-size:.85em;line-height:1.45;display:flex;position:relative;overflow:hidden}#markedown-viewer .markdown-content code:not(pre code){background-color:var(--background);border-radius:.5em;padding:.25em .4em;font-family:SFMono-Regular,Monaco,Inconsolata,Liberation Mono,Courier New,monospace;font-size:.85em}#markedown-viewer .markdown-content kbd{background-color:var(--background);border:1px solid var(--divider);box-shadow:inset 0 -2px 0 var(--divider);border-radius:.35em;padding:.1em .5em;font-family:SFMono-Regular,Monaco,Inconsolata,Liberation Mono,Courier New,monospace;font-size:.85em}#markedown-viewer .markdown-content-container .copy-code-button{color:var(--primaryColor);background:var(--background);border-radius:.45em;justify-content:center;align-items:center;padding:.2em .4em;font-size:.8em;transition:background .2s,color .2s;display:flex;position:absolute;top:.4em;right:.3em}#markedown-viewer .markdown-content-container .line-numbers{-webkit-touch-callout:none;-webkit-user-select:none;user-select:none;background-color:var(--background);border-right:1px solid var(--divider);text-align:right;color:var(--textSecondary);flex-shrink:0;min-width:2em;padding:.625em .5em .625em .75em}#markedown-viewer .markdown-content-container .line-number{white-space:nowrap;cursor:pointer;height:1.45em;line-height:1.45;transition:background-color .2s,color .2s;display:block}#markedown-viewer .markdown-content-container .line-number:hover{color:var(--primaryColor);background-color:#0000}#markedown-viewer .markdown-content-container .line-number.active{background-color:color-mix(in srgb, var(--primaryColor) 30%, transparent);color:var(--primaryColor);border-radius:.25em}#markedown-viewer .markdown-content-container .code-line{white-space:pre;min-height:1.45em;line-height:1.45;transition:background-color .2s;display:block}#markedown-viewer .markdown-content-container .code-line.highlighted{background-color:color-mix(in srgb, var(--primaryColor) 20%, transparent)}#markedown-viewer .markdown-content-container .code-content{flex:1;max-width:100%;overflow-x:auto}#markedown-viewer .markdown-content-container .code-content pre{font-family:inherit;font-size:inherit;border-radius:0;width:100%;margin:0;padding:0;line-height:1.45}#markedown-viewer .markdown-content-container .code-content code{font-family:inherit;font-size:inherit;line-height:inherit;white-space:pre;padding:.65em .5em .5em;display:block}#markedown-viewer .markdown-content-container .code-content pre code,#markedown-viewer .markdown-content-container .code-content pre code br{line-height:1.45}#markedown-viewer .markdown-content a{color:var(--primaryColor);text-decoration:underline}#markedown-viewer .markdown-content-container .code-content a{color:var(--primaryColor);font-weight:500;text-decoration:underline}#markedown-viewer .markdown-content h1,#markedown-viewer .markdown-content h2,#markedown-viewer .markdown-content h3,#markedown-viewer .markdown-content h4,#markedown-viewer .markdown-content h5,#markedown-viewer .markdown-content h6{text-wrap:balance;margin:1.4em 0 .6em;font-weight:600;line-height:1.25}#markedown-viewer .markdown-content h1{border-bottom:1px solid var(--divider);margin-top:0;padding-bottom:.3em;font-size:1.8em}#markedown-viewer .markdown-content h2{border-bottom:1px solid var(--divider);padding-bottom:.3em;font-size:1.45em}#markedown-viewer .markdown-content h3{font-size:1.2em}#markedown-viewer .markdown-content h4{font-size:1em}#markedown-viewer .markdown-content p{text-wrap:pretty;margin-top:0;margin-bottom:.85em;line-height:1.45}#markedown-viewer .markdown-content hr{border:none;border-top:1px solid var(--divider);margin:1.5em 0}#markedown-viewer .markdown-content ul{margin-top:0;margin-bottom:0;padding-bottom:.85em;padding-lefLine truncated
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
a.button[data-v-5a3a53bf]{justify-content:center;align-items:center;width:500px;max-width:100%;margin:auto;display:flex}.controls[data-v-5a3a53bf]{margin:1.5em}.search-box[data-v-5a3a53bf]{margin-bottom:1em;position:relative}.search-box .search-icon[data-v-5a3a53bf]{position:absolute;top:.25em;left:.5em}.clear-button[data-v-5a3a53bf]{position:absolute;top:50%;right:.5em;transform:translateY(-50%);min-width:auto!important;padding:.25em!important}.results-info[data-v-5a3a53bf]{color:var(--textSecondary);margin-bottom:1em;font-size:.9em}.icon-grid[data-v-5a3a53bf]{grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:.75em;margin-bottom:2em;display:grid}.icon-card[data-v-5a3a53bf]{background:var(--surfaceSecondary);border:1px solid var(--divider);border-radius:var(--borderRadius);flex-direction:column;align-items:center;padding:1em .6em;display:flex;position:relative}.icon-card.custom-icon[data-v-5a3a53bf]{border-color:var(--primaryColor);border-style:dashed}.icon-display[data-v-5a3a53bf]{color:var(--primaryColor);justify-content:center;align-items:center;height:1em;margin-bottom:.4em;font-size:2em;display:flex}.icon-name[data-v-5a3a53bf]{color:var(--textSecondary);text-align:center;overflow-wrap:break-word;font-size:.75em;line-height:1.2}.custom-badge[data-v-5a3a53bf]{background:var(--primaryColor);color:#fff;text-transform:uppercase;border-radius:.5em;padding:.25em .6em;font-size:.7em;font-weight:600;position:absolute;top:.5em;right:.5em}.empty-state[data-v-5a3a53bf]{text-align:center;color:var(--textSecondary);padding:4em 2em}.empty-state .material-symbols[data-v-5a3a53bf]{opacity:.5;margin-bottom:.5em;font-size:4em}.empty-state p[data-v-5a3a53bf]{margin-bottom:.5em;font-size:1.2em}.empty-hint[data-v-5a3a53bf]{opacity:.7;font-size:.9em}@media (width<=768px){#tool-material-icon-picker[data-v-5a3a53bf]{padding:1em}.icon-grid[data-v-5a3a53bf]{grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:.75em}.icon-card[data-v-5a3a53bf]{padding:1em .5em}.icon-display[data-v-5a3a53bf]{font-size:2em}}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.size-viewer[data-v-42452d2e]{max-width:1200px;margin-left:auto;margin-right:auto;padding:1em}.toggle-container[data-v-42452d2e]{padding:1em}.size-viewer-results[data-v-42452d2e]{margin-bottom:2em}.button[data-v-42452d2e]{align-items:center;gap:.5rem;margin-top:1rem;display:flex}.button .material-symbols[data-v-42452d2e]{font-size:1.2rem}.error-message[data-v-42452d2e]{background:color-mix(in srgb, var(--red), transparent 90%);color:var(--icon-red);border:1px solid color-mix(in srgb, var(--red), transparent 60%);border-radius:4px;margin-bottom:1rem;padding:1rem}.stats[data-v-42452d2e]{gap:2rem;margin-bottom:1rem;display:flex}.treemap[data-v-42452d2e]{background:var(--surfacePrimary);border-radius:4px;width:100%;height:600px;margin-bottom:2rem;position:relative;overflow:hidden}.treemap.has-expanded[data-v-42452d2e]{overflow:visible}.treemap-overlay[data-v-42452d2e]{background:var(--overlay);z-index:50;cursor:pointer;position:absolute;inset:0}.treemap-hit-area[data-v-42452d2e]{z-index:200;cursor:pointer;pointer-events:auto;background:0 0;border:none;outline:none;position:absolute}.treemap.has-expanded .treemap-hit-area[data-v-42452d2e]:not(.active){pointer-events:none;z-index:1}.treemap.has-expanded .treemap-hit-area.active[data-v-42452d2e]{z-index:150;pointer-events:none}.treemap-item[data-v-42452d2e]{cursor:pointer;box-sizing:border-box;pointer-events:none;border:1px solid #0000001a;flex-direction:column;justify-content:center;align-items:center;transition:left .4s cubic-bezier(.4,0,.2,1),top .4s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1),height .4s cubic-bezier(.4,0,.2,1),opacity .4s cubic-bezier(.4,0,.2,1),filter .4s cubic-bezier(.4,0,.2,1);display:flex;position:absolute;overflow:visible}.treemap-item.small-item[data-v-42452d2e]{padding:0}.treemap-item.dimmed[data-v-42452d2e]{opacity:.3;filter:brightness(.5)}.treemap-item.expanded[data-v-42452d2e]{z-index:100;pointer-events:auto;border:2px solid #ffffffe6;box-shadow:0 8px 32px #000000b3;width:50%!important;height:50%!important;top:25%!important;left:25%!important}.item-content[data-v-42452d2e]{text-align:center;color:#fff;text-shadow:1px 1px 2px #00000080;flex-direction:column;justify-content:center;align-items:center;width:100%;padding:.5rem;font-weight:500;transition:opacity .2s;display:flex;overflow:hidden}.treemap-item.expanded .item-content[data-v-42452d2e]{opacity:0;pointer-events:none;transition:opacity .2s}.item-name[data-v-42452d2e]{text-overflow:ellipsis;white-space:nowrap;max-width:100%;margin-bottom:.25rem;font-size:.85rem;overflow:hidden}.item-size[data-v-42452d2e]{opacity:.95;white-space:nowrap;font-size:.75rem}.item-percentage[data-v-42452d2e]{opacity:.85;white-space:nowrap;margin-top:.2rem;font-size:.7rem}.item-expanded[data-v-42452d2e]{opacity:0;pointer-events:none;z-index:1;flex-direction:column;justify-content:center;gap:.4rem;padding:.75rem;transition:opacity .2s,transform .2s;display:flex;position:absolute;inset:0;transform:scale(.9)}.treemap-item.expanded .item-expanded[data-v-42452d2e]{opacity:1;pointer-events:auto;z-index:200;transform:scale(1)}.expanded-field[data-v-42452d2e]{text-align:left;flex-direction:column;gap:.15rem;display:flex}.field-label[data-v-42452d2e]{opacity:.85;text-transform:uppercase;letter-spacing:.05em;color:#fff;text-shadow:1px 1px 2px #00000080;font-size:.65rem;font-weight:600}.field-value[data-v-42452d2e]{color:#fff;text-shadow:1px 1px 2px #00000080;overflow-wrap:break-word;text-overflow:ellipsis;line-clamp:2;-webkit-box-orient:vertical;max-height:3em;font-size:.8rem;font-weight:500;display:-webkit-box;overflow:hidden}.expanded-field:first-child .field-value[data-v-42452d2e]{line-clamp:1;max-height:1.5em}.expanded-field:nth-child(2) .field-value[data-v-42452d2e]{line-clamp:3;max-height:4em}.type-video[data-v-42452d2e]{background:var(--blue)}.type-image[data-v-42452d2e]{background:var(--icon-coral)}.type-audio[data-v-42452d2e]{background:var(--icon-plum)}.type-archive[data-v-42452d2e]{background:var(--icon-tan)}.type-document[data-v-42452d2e]{background:indigo}.type-binary[data-v-42452d2e]{background:var(--divider)}.type-directory[data-v-42452d2e]{background:var(--primaryColor)}.type-other[data-v-42452d2e]{background:var(--light-gray)}.legend[data-v-42452d2e]{flex-wrap:wrap;gap:1rem;margin-top:.5rem;display:flex}.legend-item[data-v-42452d2e]{align-items:center;gap:.5rem;display:flex}.legend-color[data-v-42452d2e]{border-radius:3px;width:20px;height:20px;box-shadow:0 1px 3px #0003}.empty-state[data-v-42452d2e]{text-align:center;color:var(--textSecondary);padding:4rem 2rem}.empty-state .material-symbols[data-v-42452d2e]{opacity:.3;margin-bottom:1rem;font-size:4rem}.empty-state p[data-v-42452d2e]{font-size:1.1rem}.spin[data-v-42452d2e]{animation:1s linear infinite spin-42452d2e}@keyframes spin-42452d2e{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media (width<=768px){.size-viewer[data-v-42452d2e]{padding:1rem}.treemap[data-v-42452d2e]{height:400px}.item-path[data-v-42452d2e]{font-size:.7Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
.threejs-viewer[data-v-a7499d14]{width:100%;height:100%;position:relative;overflow:hidden}.threejs-viewer canvas[data-v-a7499d14]{display:block;position:absolute;top:0;left:0;width:100%!important;height:100%!important}.loading-overlay[data-v-a7499d14],.error-overlay[data-v-a7499d14]{background:var(--background);z-index:10;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.error-content[data-v-a7499d14]{text-align:center;max-width:500px;padding:2rem}.error-content i[data-v-a7499d14]{color:var(--red);margin-bottom:1rem;font-size:64px}.error-content h3[data-v-a7499d14]{color:var(--textPrimary);margin-bottom:.5rem}.error-content p[data-v-a7499d14]{color:var(--textSecondary)}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.wopi-editor[data-v-5e12c31f]{width:100%;height:100vh;position:relative}.wopi-form[data-v-5e12c31f]{display:none}.wopi-frame[data-v-5e12c31f]{border:0;width:100%;height:100%;display:block}.wopi-loading[data-v-5e12c31f]{text-align:center;position:absolute;top:1em;left:0;right:0}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
html{line-height:1.15}body{margin:0}pre,code,kbd,samp{font-family:monospace;font-size:1em}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;white-space:normal;max-width:100%;padding:0;display:table}progress{vertical-align:baseline}[type=search]{appearance:textfield;outline-offset:-2px}[hidden]{display:none}:root{--blue:#2196f3;--dark-blue:#1e88e5;--red:#f44336;--dark-red:#be2626;--icon-red:#d23b4a;--icon-orange:#ff8c00;--icon-deep-orange:#ff6f00;--icon-yellow:#fdd34b;--icon-green:#4caf50;--icon-blue:#1d99f3;--icon-violet:#9b59b6;--icon-brown:brown;--icon-coral:lightcoral;--icon-plum:plum;--icon-tan:tan;--primaryColor:var(--blue);--light-gray:lightgray;--icon-size:3em;--safe-area-top:env(safe-area-inset-top,0px);--safe-area-left:env(safe-area-inset-left,0px);--safe-area-right:env(safe-area-inset-right,0px);--safe-area-bottom:env(safe-area-inset-bottom,0px);--header-height:calc(4em + var(--safe-area-top));--borderRadius:.85rem;--borderWidth:2px;--panel-bg:color-mix(in srgb, var(--alt-background) 65%, transparent);--panel-blur:blur(12px) invert(.1);--surfaceElevationShadow:0 1px 2px #00000012, 0 2px 4px #0000000f, 0 4px 8px #0000000d;--overlay:#00000080;--hoverOverlay:#0000001a}.dark-mode{--hoverOverlay:#ffffff1f;color:var(--textPrimary)}.border-radius{border-radius:var(--borderRadius)}.button{padding:var(--button-padding-y,.5em) var(--button-padding-x,1em);border-radius:var(--borderRadius);cursor:pointer;background:var(--button-background,var(--primaryColor));color:var(--button-color,white);box-shadow:var(--button-shadow,0 0 5px #0000000d);min-width:var(--button-min-width,2em);min-height:var(--button-min-height,2em);outline:0;justify-content:center;align-items:center;transition:transform .1s,opacity .1s,background-color .1s,box-shadow .2s;display:flex}button{background-color:var(--surfaceSecondary);min-width:var(--button-min-width,2em);min-height:var(--button-min-height,2em);border:none;justify-content:center;align-items:center;padding:.5em;transition:background-color .3s;display:flex}button:disabled{opacity:.5;cursor:not-allowed}.button--block{border-style:unset;width:100%;margin:0 0 .5em;display:block}.button--red{background:var(--red)}.button--blue{background:var(--blue)}.button--flat{color:var(--primaryColor);background:var(--surfaceSecondary);text-transform:uppercase;padding-left:1em;padding-right:1em;box-shadow:0 0}.button--flat.button--red{color:var(--dark-red)}.button--flat.button--grey{color:#6f6f6f}.button--settings-control{border-radius:50px;width:4em;min-width:4em;max-width:4em;height:34px;min-height:34px;padding:0}.button[disabled]{opacity:.5;cursor:not-allowed}:root{--fab-size:50px;--fab-size-small:36px;--fab-icon-size:24px;--fab-edge-offset:16px;--fab-edge-offset-media:20px;--fab-elevation-rest:0 1px 3px 1px #00000026, 0 1px 2px #0000004d;--fab-elevation-hover:0 2px 6px 2px #00000026, 0 1px 2px #0000004d;--fab-elevation-pressed:0 4px 8px 3px #00000026, 0 1px 3px #0000004d;--fab-shadow:var(--fab-elevation-rest);--fab-transition:.2s cubic-bezier(.2, 0, 0, 1);--fab-fade-transition:.4s ease;--fab-safe-inset:calc(var(--fab-size) + 2rem)}.has-fab-bottom-right{margin-bottom:var(--fab-safe-inset)}.input{box-sizing:border-box;border-radius:var(--borderRadius);border:var(--borderWidth) solid var(--surfaceSecondary);background:var(--background);width:100%;min-height:2.5em;color:var(--textPrimary);margin:0;padding:.5em 1em;transition:border-color .2s}.input:hover{border-color:color-mix(in srgb, var(--surfaceSecondary) 70%, var(--textPrimary))}.input:focus{border-color:var(--primaryColor);outline:none}.input:disabled{cursor:not-allowed;color:var(--textSecondary);opacity:.75}.expand-dropdown.input,.expand-dropdown.input:hover,.expand-dropdown.input:focus{box-shadow:none;background:0 0;border:none;padding:0}input:-webkit-autofill{box-shadow:0 0 0 1000px var(--background) inset;-webkit-text-fill-color:var(--textPrimary);border-color:unset}input:-webkit-autofill:hover{box-shadow:0 0 0 1000px var(--background) inset;-webkit-text-fill-color:var(--textPrimary);border-color:unset}input:-webkit-autofill:focus{box-shadow:0 0 0 1000px var(--background) inset;-webkit-text-fill-color:var(--textPrimary);border-color:unset}input:-webkit-autofill:active{box-shadow:0 0 0 1000px var(--background) inset;-webkit-text-fill-color:var(--textPrimary);border-color:unset}input[type=range]{accent-color:var(--primaryColor)}input.sizeInput:disabled{cursor:not-allowed}.sizeInputWrapper{box-sizing:border-box;border-radius:var(--borderRadius);background-color:var(--background);border:var(--borderWidth) solid var(--surfaceSecondary);color:var(--textPrimary);justify-content:center;align-items:center;margin:.5em;padding:.25em .5em;display:flex;box-shadow:0 1pxLine truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.audio-side-panel[data-v-788790b4]{background:color-mix(in srgb, var(--alt-background) 20%, var(--background) 20%);border-radius:var(--borderRadius);max-height:65vh;box-shadow:var(--surfaceElevationShadow);flex-direction:column;display:flex;overflow:hidden}.panel-tabs[data-v-788790b4]{border-bottom:1px solid var(--divider);padding:.5em}.tab-container[data-v-788790b4]{gap:.5em;display:flex;position:relative}.tab-btn[data-v-788790b4]{color:var(--textSecondary);border-radius:var(--borderRadius);cursor:pointer;z-index:1;-webkit-user-select:none;user-select:none;white-space:nowrap;background:0 0;border:none;flex:1;justify-content:center;align-items:center;gap:.3em;width:100%;min-width:0;padding:.5em;font-size:.9rem;transition:color .2s,transform .2s;display:flex;position:relative}.tab-btn>i[data-v-788790b4],.tab-btn>span+span[data-v-788790b4]{flex-shrink:0}.tab-btn>span[data-v-788790b4]:first-of-type{text-overflow:ellipsis;min-width:0;overflow:hidden}.tab-btn.active[data-v-788790b4]{color:#fff}.tab-indicator[data-v-788790b4]{background:var(--primaryColor);border-radius:var(--borderRadius);z-index:0;pointer-events:none;transition:left .35s cubic-bezier(.25,.8,.25,1),width .35s cubic-bezier(.25,.8,.25,1);position:absolute;top:0;bottom:0;left:0}.panel-content[data-v-788790b4]{flex-direction:column;flex:1;display:flex;overflow:hidden}.tab-queue[data-v-788790b4],.tab-lyrics[data-v-788790b4],.tab-visualizer[data-v-788790b4]{height:100%}.tab-queue[data-v-788790b4]{flex-direction:column;display:flex}.tab-lyrics[data-v-788790b4]{flex-direction:column;display:flex;position:relative}.lyrics-scrollable[data-v-788790b4]{flex:1;min-height:0;overflow-y:auto}.tab-queue[data-v-788790b4] .card-content{flex:1;margin:0;padding:.5em;overflow-y:auto}.tab-queue[data-v-788790b4] .card-actions{flex-shrink:0;justify-content:flex-end;padding:.25em;display:flex}.tab-queue[data-v-788790b4] .card-actions .button--flat{background:0 0}.lyrics-list[data-v-788790b4]{text-align:center;color:var(--textPrimary);padding-top:1em;padding-bottom:1em}.lyrics-meta-header[data-v-788790b4]{opacity:.55;letter-spacing:.02em;text-transform:uppercase;cursor:default;-webkit-user-select:none;user-select:none;margin:0;padding:0 0 .6em;font-size:.8rem;font-weight:600}.lyric-line[data-v-788790b4]{opacity:.6;cursor:pointer;transform-origin:50%;padding:.5em 0;font-size:1.15rem;transition:opacity .25s,color .25s,font-size .25s,transform .25s;transform:scale(1)}.lyric-line[data-v-788790b4]:hover{opacity:1}.lyric-line.active[data-v-788790b4]{opacity:1;color:var(--primaryColor);font-size:1.33rem;font-weight:700;animation:.3s lyric-line-in-788790b4}@keyframes lyric-line-in-788790b4{0%{transform:scale(.98)}to{transform:scale(1)}}.lyric-word[data-v-788790b4]{opacity:.4;transition:opacity .35s,transform .35s cubic-bezier(.25,.8,.25,1);display:inline-block;transform:scale(1)}.lyric-word.sung[data-v-788790b4]{opacity:1}.lyric-word.current[data-v-788790b4]{opacity:1;transform:scale(1.05)}.no-lyrics[data-v-788790b4]{height:100%;color:var(--textSecondary);flex-direction:column;justify-content:center;align-items:center;gap:.5em;display:flex}.no-lyrics i[data-v-788790b4]{opacity:.5;font-size:3rem}.lyric-line.no-seek[data-v-788790b4]{cursor:default}.tab-btn[data-v-788790b4]:hover:not(.active){color:var(--primaryColor);transform:scale(1.02)}.tab-visualizer[data-v-788790b4]{box-sizing:border-box;justify-content:center;align-items:center;height:100%;padding:.5em;display:flex;position:relative}.visualizer-canvas[data-v-788790b4]{border-radius:var(--borderRadius);background:#0000001f;width:100%;height:100%;display:block}@keyframes plyr-progress{to{background-position:25px 0;background-position:var(--plyr-progress-loading-size,25px) 0}}@keyframes plyr-popup{0%{opacity:.5;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes plyr-fade-in{0%{opacity:0}to{opacity:1}}.plyr{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:inherit;font-family:var(--plyr-font-family,inherit);font-variant-numeric:tabular-nums;font-weight:400;font-weight:var(--plyr-font-weight-regular,400);line-height:1.7;line-height:var(--plyr-line-height,1.7);min-width:200px;max-width:100%;text-shadow:none;z-index:0;direction:ltr;flex-direction:column;align-items:center;transition:box-shadow .3s;display:flex;position:relative}.plyr audio,.plyr iframe,.plyr video{width:100%;height:100%;display:block}.plyr button{font:inherit;line-height:inherit;width:auto}.plyr:focus{outline:0}.plyr--full-ui{box-sizing:border-box}.plyr--full-ui *,.plyr--full-ui :after,.plyr--full-ui :before{box-sizing:inherit}.plyr--full-ui a,.plyr--full-ui button,.plyr--full-ui input,.plyr--full-ui label{touch-action:manipulation}.plyr__badge{background:#4a5464;background:var(--plyr-badge-background,#4a5464);border-radius:2px;border-radius:var(--plyr-badge-border-radius,2px);color:var(--plyr-badge-text-color,#fff);font-size:9px;font-size:var(--plyr-font-size-badge,9px);padding:3px 4px;line-height:1}.plyr--fuLine truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
.transition-loading[data-v-72e6950b]{background:var(--background);z-index:10000;flex-direction:column;justify-content:center;align-items:center;transition:opacity .1s;display:flex;position:fixed;inset:0}.transition-loading .spinner[data-v-72e6950b]{text-align:center;width:70px}.transition-loading .spinner>div[data-v-72e6950b]{background-color:var(--textPrimary);border-radius:100%;width:18px;height:18px;animation:1.4s ease-in-out infinite both sk-bouncedelay-72e6950b;display:inline-block}.transition-loading .spinner .bounce1[data-v-72e6950b]{animation-delay:-.32s}.transition-loading .spinner .bounce2[data-v-72e6950b]{animation-delay:-.16s}@keyframes sk-bouncedelay-72e6950b{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.preview.transitioning[data-v-72e6950b]{opacity:.3;pointer-events:none}.pdf-wrapper[data-v-72e6950b]{width:100%;height:100%;position:relative}.pdf-wrapper .pdf[data-v-72e6950b]{border:0;width:100%;height:100%}.pdf-wrapper .floating-btn[data-v-72e6950b]{color:#fff;background:#00000080}.pdf-wrapper .floating-btn[data-v-72e6950b]:hover{background:#000000b3}.preview-buttons[data-v-72e6950b]{flex-direction:row;gap:1em;display:flex}.av-preview-wrap[data-v-72e6950b]{width:100%;height:100%;min-height:12rem}.av-preview-loading[data-v-72e6950b]{justify-content:center;align-items:center;width:100%;min-height:12rem;display:flex}.transcode-offer-message[data-v-72e6950b]{text-align:center;margin:1rem 0}.transcode-session-list[data-v-72e6950b]{text-align:center;margin:0 0 1rem;padding:0;list-style:none}.transcode-session-list li[data-v-72e6950b]{margin:.25rem 0}.transcode-session-source[data-v-72e6950b]{opacity:.7}.overlay-icon{text-shadow:0 0 3px #000c;z-index:2;color:#fff;opacity:.7;position:absolute;top:.2em;left:.2em;width:100%!important;height:100%!important;font-size:1.2em!important}.overlay-icon--bottom{pointer-events:auto;cursor:pointer;background:0 0;border:none;margin:0;padding:0;line-height:1;inset:auto 0 0 auto;width:auto!important;height:auto!important}.overlay-icon--bottom.clickable:hover{opacity:1;transform:none;box-shadow:none!important}.file-icons [aria-label^=\.],.file-icons [aria-label$=\.bak]{opacity:.33}.svg-icons{max-width:100px;display:flex}.icon{fill:currentColor;background:var(--iconBackground);will-change:auto;border-radius:.2em;padding:.1em;font-size:1.5rem;transform:translateZ(0)}.icon.active{background:var(--background)}.primary-icons{color:var(--primaryColor)}.primary-icons.active{text-shadow:0 0 1px #000}.blue-icons{color:var(--icon-blue)}.deep-blue-icons{color:#1d5fbf}.lightblue-icons{color:#87cefa}.purple-icons{color:var(--icon-violet)}.plum-icons{color:var(--icon-plum)}.yellow-icons{color:var(--icon-yellow)}.orange-icons{color:var(--icon-orange)}.red-icons{color:var(--red)}.deep-orange-icons{color:var(--icon-deep-orange)}.brown-icons{color:var(--icon-brown)}.coral-icons{color:var(--icon-coral)}.tan-icons{color:var(--icon-tan)}.green-icons{color:var(--icon-green)}.white-icons{color:#fff}.gray-icons{color:var(--divider)}.lightgray-icons{color:var(--light-gray)}#listingView.gallery .listing-item i.white-icons,.active.white-icons{color:var(--activeWhiteIcon)}.image-preview{width:var(--icon-size);height:var(--icon-size);aspect-ratio:1;background:var(--iconBackground);border-radius:.5em;justify-content:center;align-items:center;display:flex;position:relative;overflow:hidden}.gallery .image-preview{border-radius:0}.image-preview i{box-sizing:border-box;font-size:var(--icon-font-size,3em);padding:.1em;transform:translateZ(0)}.image-preview img{object-fit:cover;object-position:center;width:100%;height:100%;display:block}.image-preview .placeholder-icon{color:#fff;opacity:.7;text-shadow:0 0 3px #000c}.image-preview .threejs-icon-container{background:#000;justify-content:center;align-items:center;width:100%;height:100%;display:flex}.image-preview .threejs-icon-container canvas{display:block;width:100%!important;height:100%!important}.download-icon{cursor:pointer;color:var(--surfaceSecondary)}.icon-download{font-size:.5em}.listing-item{-webkit-touch-callout:none}.listing-item.pinned-item{border-color:color-mix(in srgb, var(--primaryColor) 35%, transparent)}.listing-item>div{min-height:1em}.hiddenFile{opacity:.5}.activebutton{height:10em}.iconActive{font-size:6em!important}.activetitle{width:9em!important;margin-right:1em!important}.half-selected{border-width:var(--borderWidth)!important;border-color:var(--primaryColor)!important;border-style:solid!important}.vue-simple-progress{background:var(--primaryColor);margin:.5em}.vue-simple-progress,.vue-simple-progress-bar{border-radius:.5em}.vue-simple-progress-text{color:#000}.tooltip-info-icon{cursor:pointer;vertical-align:middle;opacity:.7;font-size:1rem}.tooltip-info-icon:hover,.tooltip-info-icon:hover .material-symbols-outlined{font-variation-settings:"FILL" 1;opacity:1}@keyframes progress-pulse{0%,to{opacity:1}50%{opacity:.6}}.settings-group-title[data-v-7bce02d3]{justify-content:space-between;align-items:center;margin-top:.75em;pLine truncated
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,304 @@
|
||||
server:
|
||||
searchResultsLimit: 1000 # maximum requested search results across all sources (default: 1000); quick search defaults to 100, advanced search to 500; size viewer remains limited to 200 validate:gte=1
|
||||
minSearchLength: 3 # minimum length of search query to begin searching (default: 3)
|
||||
disableUpdateCheck: false # disables backend update check service
|
||||
numImageProcessors: 4 # number of concurrent image processing jobs used to create previews, default is 4.
|
||||
disablePreviews: false # disable all previews thumbnails, simple icons will be used
|
||||
disablePreviewResize: false # disable resizing of previews for faster loading over slow connections
|
||||
disableTypeDetectionByHeader: false # disable type detection by header, useful if filesystem is slow.
|
||||
logging:
|
||||
- levels: "" # separated list of log levels to enable. (eg. "info|warning|error|debug")
|
||||
apiLevels: "" # separated list of log levels to enable for the API. (eg. "info|warning|error")
|
||||
output: "stdout" # output location. (eg. "stdout" or "path/to/file.log")
|
||||
noColors: false # disable colors in the output
|
||||
json: false # output in json format
|
||||
utc: false # use UTC time in the output instead of local time.
|
||||
apiFilter: "" # regex filter that excludes matching full api paths from being logged. (eg. '/user\?id\=self') Defaults to '^/health|^/favicon.ico|^/static|^/public/static'
|
||||
sources: # validate:required,dive
|
||||
- path: "/relative/or/absolute/path" # file system path. (Can be relative) validate:required
|
||||
name: "Source Name" # display name
|
||||
config:
|
||||
denyByDefault: false # deny access unless an "allow" access rule was specifically created.
|
||||
private: false # designate as source as private -- currently just means no sharing permitted.
|
||||
readOnly: false # read-only source, changes from the UI, webdav, and API will be disabled.
|
||||
disabled: false # disable the source, this is useful so you don't need to remove it from the config file
|
||||
rules: # list of item rules to apply to specific paths
|
||||
- neverWatchPath: "" # index the folder in the first pass to get included in search, but never re-indexed.
|
||||
includeRootItem: "" # include only these items at root folder level
|
||||
fileStartsWith: "" # (global) exclude files that start with these prefixes. Eg. "archive-" or "backup-"
|
||||
folderStartsWith: "" # (global) exclude folders that start with these prefixes. Eg. "archive-" or "backup-"
|
||||
fileEndsWith: "" # (global) exclude files that end with these suffixes. Eg. ".jpg" or ".txt"
|
||||
folderEndsWith: "" # (global) exclude folders that end with these suffixes. Eg. ".thumbnails" or ".git"
|
||||
folderPath: "" # (global) exclude folders that match this path. Eg. "/path/to/folder" or "/path/to/folder/subfolder"
|
||||
filePath: "" # (global) exclude files that match this path. Eg. "/path/to/file.txt" or "/path/to/file.txt/subfile.txt"
|
||||
fileName: "" # (global) exclude files that match these names. Eg. "file.txt" or "test.csv"
|
||||
folderName: "" # (global) exclude folders that match these names. Eg. "folder" or "subfolder"
|
||||
viewable: false # Enable viewing in UI but exclude from indexing
|
||||
ignoreHidden: false # Excludes only hidden files and folders
|
||||
ignoreZeroSizeFolders: false # Excludes only folders with 0 size
|
||||
ignoreSymlinks: false # Excludes symbolic links
|
||||
defaultUserScope: "/" # defaults to root of index "/" should match folders under path
|
||||
defaultEnabled: true # should be added as a default source for new users?
|
||||
createUserDir: false # create a user directory for each user under defaultUserScope + username
|
||||
useLogicalSize: false # calculate sizes based on logical size instead of disk utilization (du -sh), folders will be 0 bytes when empty.
|
||||
defaultPermissions: # DefaultPermissions is the template for new user scopes on this source (also synced globally via Access settings).
|
||||
view: false
|
||||
download: false
|
||||
modify: false
|
||||
delete: false
|
||||
create: false
|
||||
configured: false # true when explicitly set (allows intentional deny-all)
|
||||
cacheDir: "tmp" # path to the cache directory, used for thumbnails and other cached files
|
||||
cacheDirCleanup: false # whether to automatically cleanup the cache directory. Note: docker must also mount a persistent volume to persist the cache (default: false)
|
||||
maxArchiveSize: 20 # maximum archive/unarchive size in GB. 0 means no limit. (default: 20)
|
||||
filesystem: # filesystem settings
|
||||
createFilePermission: "644" # Unix permissions like 644, 755, 2755 (default: 644) validate:required,file_permission
|
||||
createDirectoryPermission: "755" # Unix permissions like 755, 2755, 1777 (default: 755) validate:required,file_permission
|
||||
indexSqlConfig: # Index database SQL configuration
|
||||
batchSize: 1000 # number of items to batch in a single transaction, typically 500-5000. higher = faster but could use more memory.
|
||||
cacheSizeMB: 32 # size of the SQLite cache in MB
|
||||
walMode: false # enable the more complex WAL journaling mode. Slower, more memory usage, but better for deployments with constant user activity.
|
||||
disableReuse: false # enable to always create a new indexing database on startup.
|
||||
fts5: false # Enable FTS5 to provide faster search queries, but 40% slower indexing and 15% larger database size
|
||||
startupIntegrityCheck: "quickCheck" # the method used to check the integrity of the index database on startup (default: quickCheck) validate:omitempty,oneof=quickCheck probe off
|
||||
database: # SQLite database configuration
|
||||
path: "filebrowser.sqlite" # path to SQLite database file
|
||||
migrateFrom: "" # path to legacy database file for migration (optional)
|
||||
activity: # activity audit logging configuration
|
||||
disabled: false # disable semantic activity audit logging (default: false)
|
||||
retentionDays: 30 # purge activity rows older than this many days (default 30)
|
||||
flushIntervalSeconds: 10 # buffer flush interval in seconds (default 10)
|
||||
maxBufferSize: 10000 # max in-memory buffer before immediate flush (default 10000)
|
||||
quotas: # quota counter batch flush configuration
|
||||
flushIntervalSeconds: 10 # default 10
|
||||
flushMaxBuffers: 500 # default 500
|
||||
auth:
|
||||
tokenExpirationHours: 2 # time in hours each web UI session token is valid for. Default is 2 hours.
|
||||
methods:
|
||||
proxy: # validate:omitempty
|
||||
enabled: false # whether to enable this authentication method.
|
||||
adminGroup: "" # if set, users in this group will be granted admin privileges
|
||||
userGroups: # if set, only users in these groups are allowed to log in. Blocks all other users even with valid credentials.
|
||||
groupsClaim: "" # the JSON field name to read groups from. Default is "groups"
|
||||
userIdentifier: "" # the field value to use as the username. Default is "preferred_username" in oidc, "sub" in jwt. Other common values are "email" or "username", or "phone"
|
||||
disableVerifyTLS: false # disable TLS verification (insecure, for testing only)
|
||||
logoutRedirectUrl: "" # if provider logout url is provided, filebrowser will also redirect to logout url. Custom logout query params are respected.
|
||||
header: "" # required header to use for authentication. Security Warning: FileBrowser blindly accepts the header value as username.
|
||||
noauth: false # if set to true, overrides all other auth methods and disables authentication
|
||||
password: # validate:omitempty
|
||||
enabled: true
|
||||
adminUsername: "admin" # secret: admin username auto-assigned. If not set, the default is "admin".
|
||||
adminPassword: "admin" # secret: password for admin auto-assigned admin account. If set, reset on startup.
|
||||
minLength: 5 # minimum pasword length required, default is 5. validate:omitempty
|
||||
signup: false # allow signups on login page if enabled -- not secure. validate:omitempty
|
||||
recaptcha: # recaptcha config. If configured will show up the checkbox verification in the login page validate:omitempty
|
||||
host: "" # google recaptcha host, for example: https://www.google.com/recaptcha/api.js validate:required
|
||||
key: "" # v2 site key validate:required
|
||||
secret: "" # secret: v2 secret key validate:required
|
||||
enforcedOtp: false # if set to true, TOTP is enforced for all password users users. Otherwise, users can choose to enable TOTP.
|
||||
oidc: # validate:omitempty
|
||||
enabled: false # whether to enable this authentication method.
|
||||
adminGroup: "" # if set, users in this group will be granted admin privileges
|
||||
userGroups: # if set, only users in these groups are allowed to log in. Blocks all other users even with valid credentials.
|
||||
groupsClaim: "groups" # the JSON field name to read groups from. Default is "groups"
|
||||
userIdentifier: "preferred_username" # the field value to use as the username. Default is "preferred_username" in oidc, "sub" in jwt. Other common values are "email" or "username", or "phone"
|
||||
disableVerifyTLS: false # disable TLS verification (insecure, for testing only)
|
||||
logoutRedirectUrl: "" # if provider logout url is provided, filebrowser will also redirect to logout url. Custom logout query params are respected.
|
||||
clientId: "" # secret: client id of the OIDC application
|
||||
clientSecret: "" # secret: client secret of the OIDC application
|
||||
issuerUrl: "" # authorization URL of the OIDC provider
|
||||
scopes: "openid email profile" # scopes to request from the OIDC provider
|
||||
ldap: # validate:omitempty
|
||||
enabled: false # whether to enable this authentication method.
|
||||
adminGroup: "" # if set, users in this group will be granted admin privileges
|
||||
userGroups: # if set, only users in these groups are allowed to log in. Blocks all other users even with valid credentials.
|
||||
groupsClaim: "" # the JSON field name to read groups from. Default is "groups"
|
||||
userIdentifier: "" # the field value to use as the username. Default is "preferred_username" in oidc, "sub" in jwt. Other common values are "email" or "username", or "phone"
|
||||
disableVerifyTLS: false # disable TLS verification (insecure, for testing only)
|
||||
logoutRedirectUrl: "" # if provider logout url is provided, filebrowser will also redirect to logout url. Custom logout query params are respected.
|
||||
server: "" # scheme://host:port of the LDAP server (e.g. ldap://localhost:389)
|
||||
baseDN: "" # LDAP search base DN (e.g. dc=ldap,dc=goauthentik,dc=io)
|
||||
userDN: "" # Bind DN for service account (e.g. cn=admin,ou=users,dc=ldap,dc=goauthentik,dc=io)
|
||||
userPassword: "" # secret: bind password for the LDAP service account
|
||||
userFilter: "" # Search filter for finding user by username. Default (&(cn=%s)(objectClass=user)); override e.g. (email=%s) or (sAMAccountName=%s) for other directories.
|
||||
jwt: # validate:omitempty
|
||||
enabled: false # whether to enable this authentication method.
|
||||
adminGroup: "" # if set, users in this group will be granted admin privileges
|
||||
userGroups: # if set, only users in these groups are allowed to log in. Blocks all other users even with valid credentials.
|
||||
groupsClaim: "" # the JSON field name to read groups from. Default is "groups"
|
||||
userIdentifier: "" # the field value to use as the username. Default is "preferred_username" in oidc, "sub" in jwt. Other common values are "email" or "username", or "phone"
|
||||
disableVerifyTLS: false # disable TLS verification (insecure, for testing only)
|
||||
logoutRedirectUrl: "" # if provider logout url is provided, filebrowser will also redirect to logout url. Custom logout query params are respected.
|
||||
header: "" # HTTP header to look for JWT token (e.g. X-JWT-Assertion). Default is "X-JWT-Assertion"
|
||||
secret: "" # secret: Shared secret key/bytes for verifying JWT token signatures (required, eg PUBLIC KEY, RSA PUBLIC KEY, EC PUBLIC KEY, or CERTIFICATE)
|
||||
algorithm: "" # JWT signing algorithm (HS256, HS384, HS512, RS256, ES256). Default is "HS256"
|
||||
passkey: # validate:omitempty
|
||||
enabled: false # whether to enable passkey MFA
|
||||
rpDisplayName: "" # the Relying Party display name (defaults to frontend name)
|
||||
rpId: "" # the Relying Party ID; if empty, auto-derived from base URL host
|
||||
rpOrigins: # allowed origins; if empty, auto-derived from base URL
|
||||
loginButtonText: "" # custom text for the passkey login button
|
||||
key: "" # secret: HMAC key for JWT tokens. If unset in config/env, one is generated and stored in the application database.
|
||||
totpSecret: "" # secret: secret used to encrypt TOTP secrets
|
||||
frontend:
|
||||
name: "FileBrowser Quantum" # display name, PWA installation name is capped at 30 characters
|
||||
disableDefaultLinks: false # disable default links in the sidebar
|
||||
disableUsedPercentage: false # disable used percentage for the sources in the sidebar
|
||||
externalLinks:
|
||||
- text: "(untracked)" # the text to display on the link validate:required
|
||||
title: "untracked" # the title to display on hover
|
||||
url: "https://github.com/gtsteffaniak/filebrowser/releases/" # the url to link to validate:required
|
||||
- text: "Help" # the text to display on the link validate:required
|
||||
title: "" # the title to display on hover
|
||||
url: "help prompt" # the url to link to validate:required
|
||||
disableNavButtons: false # disable the nav buttons in the sidebar
|
||||
styling:
|
||||
disableEventThemes: false # disable the event based themes,
|
||||
customCSS: "" # if a valid path to a css file is provided, it will be applied for all users. (eg. "reduce-rounded-corners.css")
|
||||
lightBackground: "#f5f5f5" # specify a valid CSS color property value to use as the background color in light mode
|
||||
darkBackground: "#141D24" # Specify a valid CSS color property value to use as the background color in dark mode
|
||||
customThemes: # A list of custom css files that each user can select to override the default styling. if "default" is key name then it will be the default option.
|
||||
alternative:
|
||||
description: "Reduce rounded corners" # The description of the theme to display in the UI.
|
||||
css: "reduce-rounded-corners.css" # The css file path and filename to use for the theme.
|
||||
default:
|
||||
description: "The default theme" # The description of the theme to display in the UI.
|
||||
css: "" # The css file path and filename to use for the theme.
|
||||
favicon: "" # path to a favicon to use for the frontend
|
||||
description: "FileBrowser Quantum is a file manager for the web which can be used to manage files on your server" # description that shows up in html head meta description
|
||||
loginIcon: "" # path to an image file for the login page icon
|
||||
loginButtonText: "" # text to display on the login button
|
||||
oidcLoginButtonText: "" # text to display on the OIDC login button
|
||||
disablePWAInstall: false # disable the PWA install button in the sidebar
|
||||
userDefaults: # optional signup/CLI defaults; per-user values are managed in the UI
|
||||
sidebar:
|
||||
disableQuickToggles: false # disable the quick toggles in the sidebar
|
||||
hideFileActions: false # hide the file actions in the sidebar
|
||||
disableHideOnPreview: false # keep sidebar open when previewing files (was preview.disableHideSidebar)
|
||||
sticky: true # keep sidebar open when navigating
|
||||
hideFiles: false # hide files in the sidebar tree navigation, when true, will show only directories
|
||||
showTools: true # show sidebar links with category "tool"; default is true
|
||||
listing:
|
||||
deleteWithoutConfirming: false # delete files without confirmation
|
||||
promptRightCloseButton: false # show the prompts close button on the right
|
||||
dateFormat: false # when false, the date is relative, when true, the date is an exact timestamp
|
||||
showHidden: false # show hidden files in the UI. On windows this includes files starting with a dot and windows hidden files
|
||||
quickDownload: false # show icon to download in one click
|
||||
showSelectMultiple: false # show select multiple files on desktop
|
||||
singleClick: false # open directory on single click, also enables middle click to open in new tab
|
||||
hideFileExt: "" # space separated list of file extensions to hide in UI
|
||||
showCopyPath: false # show copy path button in the context menu
|
||||
showTypeColumn: false # show the Type column in list and compact views
|
||||
showCreationDateColumn: false # show the Creation date column in list and compact views
|
||||
deleteAfterArchive: true # delete source files after successful creation/extraction of archives
|
||||
viewMode: "normal" # view mode to use: eg. normal, list, grid, or compact
|
||||
gallerySize: 3 # 0-9 - the size of the gallery thumbnails
|
||||
newFileTemplate: # list of custom filenames that will be used as template for new files
|
||||
- ""
|
||||
preview:
|
||||
image: true # show thumbnails for image files
|
||||
video: true # show thumbnails for video files
|
||||
audio: true # show thumbnails for audio files
|
||||
motionVideoPreview: true # show multiple frames for videos in thumbnail preview when hovering
|
||||
office: true # show thumbnails for office files
|
||||
popup: true # show larger popup preview when hovering over thumbnail
|
||||
disablePreviewExt: "" # comma separated list of file extensions to disable preview for
|
||||
highQuality: true # high quality preview thumbnails
|
||||
folder: true # show thumbnails for folders that have previewable contents
|
||||
models: true # show live thumbnails for 3D models files
|
||||
fileViewer:
|
||||
autoplayMedia: true # autoplay media files in preview
|
||||
disableViewingExt: "" # comma separated list of file extensions to disable viewing for
|
||||
disableOnlyOfficeExt: ".md .txt .pdf .html .xml" # list of file extensions to disable onlyoffice editor for
|
||||
preferEditorForMarkdown: false # prefer editor first for markdown files instead of the Markdown Viewer
|
||||
debugOffice: false # debug onlyoffice editor
|
||||
search:
|
||||
disableOptions: false # disable the search options in the search bar
|
||||
ui:
|
||||
darkMode: true # should dark mode be enabled
|
||||
themeColor: "var(--blue)" # theme color to use: eg. #ff0000, or var(--red), var(--purple), etc
|
||||
customTheme: "" # Name of theme to use chosen from custom themes config
|
||||
locale: "en" # language to use: eg. de, en, or fr
|
||||
fileLoading:
|
||||
maxConcurrentUpload: 10
|
||||
uploadChunkSizeMb: 10
|
||||
clearAll: false
|
||||
downloadChunkSizeMb: 0
|
||||
account:
|
||||
permissions:
|
||||
api: false # allow api access
|
||||
admin: false # allow admin access
|
||||
share: false # allow sharing files
|
||||
realtime: false # allow realtime updates
|
||||
lockPassword: false # disable the user from changing their password
|
||||
requirePasswordChange: false # force password change at next password login
|
||||
disableSettings: false # disable the user from viewing the settings page
|
||||
loginMethod: "password" # login method to use: eg. password, proxy, oidc
|
||||
disableUpdateNotifications: false # disable update notifications banner for admin users
|
||||
showAdvancedProfile: false # show advanced profile settings in the user profile page
|
||||
maxConcurrentTranscodes: 1 # per-user concurrent transcode sessions (default: 1)
|
||||
shareDefaults: # optional defaults for new shares; managed in the UI
|
||||
shareTheme: ""
|
||||
disableAnonymous: false
|
||||
disableThumbnails: false
|
||||
keepAfterExpiration: false
|
||||
themeColor: ""
|
||||
title: ""
|
||||
description: ""
|
||||
favicon: ""
|
||||
quickDownload: false
|
||||
hideNavButtons: false
|
||||
disableSidebar: false
|
||||
disableShareCard: false
|
||||
enforceDarkLightMode: ""
|
||||
viewMode: ""
|
||||
enableOnlyOffice: false
|
||||
shareType: ""
|
||||
allowDelete: false
|
||||
allowCreate: false
|
||||
allowModify: false
|
||||
disableFileViewer: false
|
||||
disableDownload: false
|
||||
allowReplacements: false
|
||||
sidebarLinks:
|
||||
showHidden: false
|
||||
disableLoginOption: false
|
||||
maxBandwidth: 0
|
||||
allowedUsernames:
|
||||
perUserDownloadLimit: false
|
||||
downloadsLimit: 0
|
||||
quotaLimitBytes: 0
|
||||
hideFileExt: ""
|
||||
banner: ""
|
||||
integrations:
|
||||
office: # validate:omitempty
|
||||
url: "" # The URL to the office editor (OnlyOffice Document Server or Collabora Online), needs to be accessible to the user. validate:required
|
||||
internalUrl: "" # An optional internal address that the filebrowser server can use to communicate with the office editor, could be useful to bypass proxy.
|
||||
secret: "" # secret: OnlyOffice only, the JWT secret shared with the Document Server. Unused with collabora: editing sessions are signed with a key derived from auth.key, and Collabora trusts FileBrowser through its storage.wopi.host allow-list, where FileBrowser's host must be added. validate:required_unless=Product collabora
|
||||
product: "" # office editor behind url: onlyoffice (default) or collabora validate:omitempty,oneof=onlyoffice collabora
|
||||
tokenExpirationHours: 10 # collabora only: lifetime of an editing session, in hours (default: 10)
|
||||
viewOnly: false # view only mode for the office editor
|
||||
media: # validate:omitempty
|
||||
ffmpegPath: "" # path to ffmpeg directory with ffmpeg and ffprobe (eg. /usr/local/bin)
|
||||
skipHwCheck: false # SkipHwCheck skips the ffmpeg hardware codec probe entirely (software-only).
|
||||
# Config-only, not a runtime setting — advanced use, e.g. keeping the
|
||||
# multi-second probe out of CI startup.
|
||||
http:
|
||||
socket: "" # socket to listen on - eg. /var/run/filebrowser.sock
|
||||
tlsKey: "" # path to TLS key
|
||||
tlsCert: "" # path to TLS cert
|
||||
port: 80 # port to listen on
|
||||
listen: "" # address to listen on (default: 0.0.0.0)
|
||||
baseURL: "/" # base URL for the server, the subpath that the server is running on.
|
||||
externalUrl: "" # used by share links if set (eg. http://mydomain.com)
|
||||
internalUrl: "" # used by integrations if set, this is the base domain that an integration service will use to communicate with filebrowser (eg. http://localhost:8080)
|
||||
disableWebDAV: false # disable webdav support (default: false)
|
||||
trustProxyHeaders: false # honor X-Forwarded-* and X-Real-IP from a reverse proxy (default: false)
|
||||
trustedProxies: # IPs/CIDRs of trusted proxies; forwarded client headers are only honored when the socket peer matches one of these (default: empty = forwarded headers ignored)
|
||||
unixSocketTrustedUIDs: # UnixSocketTrustedUIDs and UnixSocketTrustedGIDs list peer credentials (Linux SO_PEERCRED) allowed to
|
||||
# present X-Forwarded-* on a filesystem http.socket listener. When both are empty, forwarded headers
|
||||
# are not trusted on Unix sockets. Abstract sockets (@name) never trust forwarded headers.
|
||||
unixSocketTrustedGIDs:
|
||||
socketFileMode: "" # octal mode for filesystem http.socket (default "0660"); ignored for abstract sockets
|
||||
socketGroup: "" # optional group name or numeric gid for the socket file (filesystem sockets only)
|
||||
disableRateLimit: false # turns off built-in auth route rate limiting and failed-login lockout (default false).
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* LoadingSpinner.vue + initial index.html shell.
|
||||
* Embedded in index.html via the backend; also @import'd from src/css/styles.css for the Vue app.
|
||||
*
|
||||
* Spinner glyph: filled SVG path (data URI mask) so ring thickness tapers along the arc — not alpha-only.
|
||||
* Regenerate: polygon from outer/inner circular arcs (~120° sweep) in viewBox 0 0 100 100; mirror X if spin/orientation needs flipping.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Core box */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader {
|
||||
box-sizing: border-box;
|
||||
margin: 0 auto;
|
||||
border: none;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Tapered ring: solid fill + SVG mask (true stroke-width taper, not alpha) */
|
||||
/* Path is mirrored so the thick head leads when rotating clockwise (W3C +angle = CW). */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader__comet {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
direction: ltr;
|
||||
background-color: var(--spinner-color, var(--primaryColor));
|
||||
/* Mirrored annulus-sector polygon, 120° sweep, viewBox 0 0 100 100 */
|
||||
--fb-loader-taper-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%3E%3Cpath%20fill%3D%22black%22%20d%3D%22M57.47%2C7.65%20L54.99%2C7.29%20L52.50%2C7.07%20L50.00%2C7.00%20L47.50%2C7.07%20L45.01%2C7.29%20L42.53%2C7.65%20L40.08%2C8.16%20L37.67%2C8.81%20L35.29%2C9.59%20L32.97%2C10.52%20L30.70%2C11.57%20L28.50%2C12.76%20L26.37%2C14.07%20L24.32%2C15.51%20L22.36%2C17.06%20L20.49%2C18.72%20L18.72%2C20.49%20L17.06%2C22.36%20L15.51%2C24.32%20L14.07%2C26.37%20L12.76%2C28.50%20L11.57%2C30.70%20L10.52%2C32.97%20L9.59%2C35.29%20L8.81%2C37.67%20L8.16%2C40.08%20L7.65%2C42.53%20L7.29%2C45.01%20L7.07%2C47.50%20L7.00%2C50.00%20L7.07%2C52.50%20L7.29%2C54.99%20L7.65%2C57.47%20L8.16%2C59.92%20L8.81%2C62.33%20L9.59%2C64.71%20L10.16%2C64.50%20L9.56%2C62.11%20L9.12%2C59.69%20L8.81%2C57.26%20L8.65%2C54.83%20L8.63%2C52.41%20L8.75%2C50.00%20L9.01%2C47.61%20L9.41%2C45.26%20L9.94%2C42.94%20L10.61%2C40.66%20L11.40%2C38.44%20L12.32%2C36.28%20L13.36%2C34.19%20L14.51%2C32.18%20L15.77%2C30.24%20L17.14%2C28.39%20L18.60%2C26.63%20L20.16%2C24.96%20L21.81%2C23.40%20L23.53%2C21.95%20L25.33%2C20.60%20L27.20%2C19.37%20L29.12%2C18.26%20L31.10%2C17.26%20L33.12%2C16.39%20L35.18%2C15.64%20L37.27%2C15.02%20L39.38%2C14.52%20L41.50%2C14.15%20L43.64%2C13.91%20L45.77%2C13.79%20L47.89%2C13.79%20L50.00%2C13.92%20L52.09%2C14.18%20L54.14%2C14.55%20L56.16%2C15.04Z%22%2F%3E%3C%2Fsvg%3E");
|
||||
|
||||
mask-image: var(--fb-loader-taper-mask);
|
||||
mask-size: contain;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
transform-origin: 50% 50%;
|
||||
transform-box: fill-box;
|
||||
animation: fb-spinner-rotation var(--animation-duration, 1s) linear infinite;
|
||||
}
|
||||
|
||||
@keyframes fb-spinner-rotation {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Size variants */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader--xsmall {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.loader--small {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.loader--medium {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.loader--large {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Initial full-page shell (index.html) */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
body > .loader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
transition: 0.1s ease opacity;
|
||||
pointer-events: none;
|
||||
border: none;
|
||||
animation: none;
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body > .loader.loader--xsmall,
|
||||
body > .loader.loader--small,
|
||||
body > .loader.loader--medium,
|
||||
body > .loader.loader--large {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body > .loader .loader__comet {
|
||||
position: relative;
|
||||
inset: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
body > .loader.loader--xsmall .loader__comet {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
body > .loader.loader--small .loader__comet {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
body > .loader.loader--medium .loader__comet {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
body > .loader.loader--large .loader__comet {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
|
||||
body > .loader.done {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Pulse mode */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader--pulse {
|
||||
border: none;
|
||||
background: var(--spinner-color, var(--primaryColor));
|
||||
animation: fb-pulse-scale var(--animation-duration, 0.75s) ease infinite;
|
||||
}
|
||||
|
||||
@keyframes fb-pulse-scale {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Bounce mode */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader--bounce {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.3em;
|
||||
border: none;
|
||||
background: transparent;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.loader--bounce::before,
|
||||
.loader--bounce::after {
|
||||
content: "";
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
background-color: var(--spinner-color, var(--primaryColor));
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
animation: fb-bounce-scale var(--animation-duration, 0.75s) infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.loader--bounce::before {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.loader--bounce::after {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
.loader--bounce .bounce-dot {
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
background-color: var(--spinner-color, var(--primaryColor));
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
animation: fb-bounce-scale var(--animation-duration, 0.75s) infinite ease-in-out both;
|
||||
}
|
||||
|
||||
@keyframes fb-bounce-scale {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: scale(0);
|
||||
opacity: 0.5;
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.loader--bounce.loader--xsmall::before,
|
||||
.loader--bounce.loader--xsmall::after,
|
||||
.loader--bounce.loader--xsmall .bounce-dot {
|
||||
width: 0.3em;
|
||||
height: 0.3em;
|
||||
}
|
||||
|
||||
.loader--bounce.loader--small::before,
|
||||
.loader--bounce.loader--small::after,
|
||||
.loader--bounce.loader--small .bounce-dot {
|
||||
width: 0.45em;
|
||||
height: 0.45em;
|
||||
}
|
||||
|
||||
.loader--bounce.loader--medium::before,
|
||||
.loader--bounce.loader--medium::after,
|
||||
.loader--bounce.loader--medium .bounce-dot {
|
||||
width: 0.6em;
|
||||
height: 0.6em;
|
||||
}
|
||||
|
||||
.loader--bounce.loader--large::before,
|
||||
.loader--bounce.loader--large::after,
|
||||
.loader--bounce.loader--large .bounce-dot {
|
||||
width: 0.9em;
|
||||
height: 0.9em;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Placeholder mode */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.loader--placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
border: none;
|
||||
animation: none;
|
||||
width: auto;
|
||||
height: auto;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.placeholder-item {
|
||||
background-color: rgb(128 128 128 / 10%);
|
||||
animation: fb-placeholder-ripple 1.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite,
|
||||
fb-placeholder-bg-fade 2s ease-in-out infinite;
|
||||
display: block;
|
||||
border-radius: 0.25em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
|
||||
box-shadow 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.placeholder-item-1 {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.placeholder-item-2 {
|
||||
animation-delay: 0.36s;
|
||||
}
|
||||
|
||||
.placeholder-item-3 {
|
||||
animation-delay: 0.72s;
|
||||
}
|
||||
|
||||
@keyframes fb-placeholder-ripple {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
25% {
|
||||
transform: scale(1.07);
|
||||
box-shadow: inset 0 -3em 3em rgb(172 172 172 / 21.1%),
|
||||
0 0 0 2px var(--alt-background), 0 4px 12px rgb(0 0 0 / 15%);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.97);
|
||||
box-shadow: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fb-placeholder-bg-fade {
|
||||
0% {
|
||||
background-color: rgb(128 128 128 / 10%);
|
||||
}
|
||||
50% {
|
||||
background-color: rgb(128 128 128 / 40%);
|
||||
}
|
||||
100% {
|
||||
background-color: rgb(128 128 128 / 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.loader--placeholder.loader--xsmall .placeholder-item {
|
||||
width: 30px;
|
||||
height: 7.5px;
|
||||
min-width: 30px;
|
||||
min-height: 7.5px;
|
||||
}
|
||||
|
||||
.loader--placeholder.loader--small .placeholder-item {
|
||||
width: 150px;
|
||||
height: 12.5px;
|
||||
min-width: 150px;
|
||||
min-height: 12.5px;
|
||||
}
|
||||
|
||||
.loader--placeholder.loader--medium .placeholder-item {
|
||||
width: 200px;
|
||||
height: 25px;
|
||||
min-width: 200px;
|
||||
min-height: 25px;
|
||||
}
|
||||
|
||||
.loader--placeholder.loader--large .placeholder-item {
|
||||
width: 400px;
|
||||
height: 50px;
|
||||
min-width: 400px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Standard portrait mobile responsive fix */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
@media (width <= 768px) {
|
||||
.loader--placeholder.loader--large .placeholder-item {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* `--background` is intentionally set here as a placeholder for stylelint */
|
||||
:root {
|
||||
--background: #f5f5f5;
|
||||
--alt-background: #ddd;
|
||||
--surfacePrimary: #ebebeb;
|
||||
--surfaceSecondary: lightgray;
|
||||
--textPrimary: #546e7a;
|
||||
--textSecondary: gray;
|
||||
--divider: rgb(176 176 176 / 50%);
|
||||
--iconBackground: #ddd;
|
||||
--activeWhiteIcon: gray;
|
||||
}
|
||||
|
||||
.dark-mode {
|
||||
--background: #141D24;
|
||||
--alt-background: #283136;
|
||||
--surfacePrimary: #20292F;
|
||||
--surfaceSecondary: #3A4147;
|
||||
--divider: rgb(255 255 255 / 12%);
|
||||
--textPrimary: rgb(255 255 255 / 87%);
|
||||
--textSecondary: rgb(255 255 255 / 60%);
|
||||
--iconBackground: #171718;
|
||||
--activeWhiteIcon: white;
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user