v0.3.4 initial release
This commit is contained in:
parent
d2a3e50d37
commit
0270711a22
|
@ -7,22 +7,22 @@ on:
|
|||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
|
||||
jobs:
|
||||
test_frontend:
|
||||
name: Push release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3.0.0
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.0.0
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.playwright
|
||||
push: false
|
||||
#test_frontend:
|
||||
# name: Push release
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v4
|
||||
# - name: Set up QEMU
|
||||
# uses: docker/setup-qemu-action@v3.0.0
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3.0.0
|
||||
# - name: Build
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# context: .
|
||||
# file: ./Dockerfile.playwright
|
||||
# push: false
|
||||
push_pr_to_registry:
|
||||
name: Push PR
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -61,7 +61,7 @@ jobs:
|
|||
JSON="${{ steps.meta.outputs.tags }}"
|
||||
# Use jq to remove 'v' from the version field
|
||||
JSON=$(echo "$JSON" | sed 's/filebrowser:v/filebrowser:/')
|
||||
echo "CLEANED_TAG=$JSON" >> $GITHUB_OUTPUT
|
||||
echo "cleaned_tag=$JSON" >> $GITHUB_OUTPUT
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
|
@ -72,5 +72,5 @@ jobs:
|
|||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
file: ./Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.modify-json.outputs.CLEANED_TAG }}
|
||||
tags: ${{ steps.modify-json.outputs.cleaned_tag }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
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).
|
||||
|
||||
## v0.3.4
|
||||
|
||||
**Bugfixes**:
|
||||
- Safari right-click actions.
|
||||
- Some small image viewer behavior
|
||||
- Progressive webapp "install to homescreen" fix.
|
||||
|
||||
## v0.3.3
|
||||
|
||||
**New Features**
|
||||
|
|
21
README.md
21
README.md
|
@ -10,16 +10,15 @@
|
|||
</p>
|
||||
|
||||
> [!Note]
|
||||
> Starting with v0.3.3, configuration file mapping is different to support non-root user. Now, the default config file name is `config.yaml` and in docker the path is `/home/filebrowser/config.yaml` and `/home/filebrowser/<database_file>`. Please read the usage below to properly update your config to point the new config location.
|
||||
> Starting with v0.3.3, configuration file mapping is different to support non-root user. Now, the default config file name is `config.yaml` and in docker the path is `/home/filebrowser/config.yaml` and `/home/filebrowser/<database_file>`. Please read the usage below to properly update your config to point the new config location. (open an issue for any help needed)
|
||||
|
||||
> [!WARNING]
|
||||
> - There is no stable version yet. Always check release notes for bug fixes on functionality that may have been changed. If you notice any unexpected behavior -- please open an issue to have it fixed soon.
|
||||
> - If on windows, please use docker. The windows binary is unstable and may not work.
|
||||
|
||||
FileBrowser Quantum is a fork of the file browser opensource project with the following changes:
|
||||
|
||||
1. [x] Indexes files efficiently. See [indexing readme](./docs/indexing.md)
|
||||
- Real-time search results as you type
|
||||
1. [x] Indexes files efficiently. (See [indexing readme](./docs/indexing.md) for more info.)
|
||||
- Real-time search results as you type!
|
||||
- Search supports file/folder sizes and many file type filters.
|
||||
- Enhanced interactive results that show file/folder sizes.
|
||||
2. [x] Revamped and simplified GUI navbar and sidebar menu.
|
||||
|
@ -27,21 +26,21 @@ FileBrowser Quantum is a fork of the file browser opensource project with the fo
|
|||
styles.
|
||||
- Many graphical and user experience improvements.
|
||||
- right-click context menu
|
||||
3. [x] Revamped and simplified configuration via `filebrowser.yml` config file.
|
||||
3. [x] Revamped and simplified configuration via `config.yaml` config file.
|
||||
4. [x] Better listing browsing
|
||||
- Switching view modes is instant
|
||||
- Folder sizes are shown as well
|
||||
- Changing Sort order is instant
|
||||
- The entire directory is loaded in 1/3 the time
|
||||
5. [x] Developer API support
|
||||
- Can create long-live API Tokens.
|
||||
- Ability to create long-live API Tokens.
|
||||
- Helpful Swagger page available at `/swagger` endpoint.
|
||||
|
||||
Notable features that this fork *does not* have (removed):
|
||||
|
||||
- jobs/runners are not supported yet (planned).
|
||||
- shell commands are completely removed and will not be returned.
|
||||
- themes and branding are not fully supported yet (planned).
|
||||
- Themes and branding are not fully supported yet (planned).
|
||||
- see feature matrix below for more.
|
||||
- pagination for directory items, so large directories with more than 100,000 items may be slow to load or not load at all.
|
||||
|
||||
|
@ -67,10 +66,11 @@ focus of this fork is on a few key principles:
|
|||
- Minimize external dependencies and standard library usage.
|
||||
- Of course -- adding much-needed features.
|
||||
|
||||
For more questions, see the [Q&A Readme](./docs/questions.md)
|
||||
|
||||
## Look
|
||||
|
||||
One way you can observe the improved user experience is how I changed
|
||||
the UI. The Navbar is simplified to a three-component system :
|
||||
The UI has a simple three-component navigation system :
|
||||
|
||||
1. (Left) The slide-out action panel button
|
||||
2. (Middle) The powerful search bar.
|
||||
|
@ -162,7 +162,7 @@ There are very few commands available. There are 3 actions done via the command
|
|||
|
||||
## API Usage
|
||||
|
||||
FileBrowser Quantum allows for the creation of API tokens which can create users, access file information, and update user settings just like what can be done from the UI. You can create API tokens from the settings page via "API Management" section. This section will only show up if the user has "API" permissions, which can be granted by editing the user in user management.
|
||||
API tokens can be created to perform actions, access file information, and update user settings just like what can be done from the UI. You can create API tokens from the settings page via "API Management" section. This section will only show up if the user has "API" permissions, which can be granted by editing the user in user management.
|
||||
|
||||
Regardless of whether a user has API permissions, anyone can visit the swagger page which is found at `/swagger`. This swagger page uses a short-live token (2-hour exp) that the UI uses, but allows for quick access to all the API's and their described usage and requirements:
|
||||
|
||||
|
@ -191,7 +191,6 @@ configuration options and other help.
|
|||
|
||||
## Migration from the original filebrowser
|
||||
|
||||
If you currently use the original filebrowser but want to try using this.
|
||||
I would recommend that you start fresh without reusing the database. However,
|
||||
If you want to migrate your existing database to FileBrowser Quantum,
|
||||
visit the [migration
|
||||
|
|
|
@ -24,15 +24,15 @@ builds:
|
|||
- upx {{ .Path }} # Compress the binary with UPX
|
||||
|
||||
# Build configuration for windows without arm
|
||||
- id: windows
|
||||
ldflags: *ldflags
|
||||
main: main.go
|
||||
binary: filebrowser
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
# - id: windows
|
||||
# ldflags: *ldflags
|
||||
# main: main.go
|
||||
# binary: filebrowser
|
||||
# goos:
|
||||
# - windows
|
||||
# goarch:
|
||||
# - amd64
|
||||
# - arm64
|
||||
|
||||
archives:
|
||||
- name_template: "{{.Os}}-{{.Arch}}{{if .Arm}}v{{.Arm}}{{end}}-{{ .ProjectName }}"
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
@ -52,7 +51,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
|
|||
"BaseURL": config.Server.BaseURL,
|
||||
"Version": version.Version,
|
||||
"CommitSHA": version.CommitSHA,
|
||||
"StaticURL": path.Join(config.Server.BaseURL, "static"),
|
||||
"StaticURL": config.Server.BaseURL + "static",
|
||||
"Signup": settings.Config.Auth.Signup,
|
||||
"NoAuth": config.Auth.Method == "noauth",
|
||||
"AuthMethod": config.Auth.Method,
|
||||
|
@ -62,6 +61,7 @@ func handleWithStaticData(w http.ResponseWriter, r *http.Request, file, contentT
|
|||
"EnableThumbs": config.Server.EnableThumbnails,
|
||||
"ResizePreview": config.Server.ResizePreview,
|
||||
"EnableExec": config.Server.EnableExec,
|
||||
"ReCaptchaHost": config.Auth.Recaptcha.Host,
|
||||
}
|
||||
|
||||
if config.Frontend.Files != "" {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
# Q&A topics
|
||||
|
||||
## When will there be a stable release?
|
||||
|
||||
Please see [roadmap](roadmap.md) for future plans -- yes a stable release will eventually come.
|
||||
|
||||
## Can you add FileBrowser Quantum features to the original file browser?
|
||||
|
||||
This "Quantum" version fork was created because I wanted certain features that are a dramatic and opinionated departure from the OG filebrowser. If you look at the original filebrowser repo pull requests, you will find there are many basic features that remain open for many months or years with very little attention.
|
||||
|
||||
If the original filebrowser maintainers were more active and if I didn't have to worry about spending months or years playing politics about the concequences of these drastic changes, I would contribute to the original repository.
|
||||
|
||||
However, **I will not make an modifications to the original filebrowser**, for these reasons:
|
||||
|
||||
1. My changes are opinionated and I want full control over the experience (and consequences) of the changes. For example I removed the terminal, runners, command line flags, and more. These are changes that would probably be highly contested. I think the experience is better without them, or with the changes -- and I hope you agree.
|
||||
2. Contributing to that open source project takes a long time, I may never see my changes actually make it in and don't want to waste my time trying for years, I only have so much time.
|
||||
3. This project was originally a fork, but that quickly changed. There are hundereds of thousands of changes and complete departures from the original codebase. I can't simply "port" the features I write on this repo over to the OG file browser.
|
||||
|
||||
Both of these repos being open source means YOU can migrate these features if you want! Feel free to spend the effort and do so for the community. I am not the only one capable of doing it and I encourage this if you have the time, energy, and knowhow to do it.
|
||||
|
||||
## I notice a lot of things that don't work like the original file browser repo, how can I get this fixed?
|
||||
|
||||
Please open issues and/or pull requests from a forked repo if you notice issues that should be fixed. Some changes are intentional and I may have left things broken. For example, I am not fully confident user rules work in 100% of places. When you notice things, please let me know and I will check if its an intentional change or a bug.
|
||||
|
||||
## Is there a way to donate or support this project?
|
||||
|
||||
Nope... not yet! It's still "unfinished" in my opinion, so I don't want to ask for any money from it. But if you have a strong desire to donate, email info@quantumx-apps.com to get in touch.
|
||||
|
||||
## Is there an email or phone I can contact?
|
||||
|
||||
Yes - Please contact info@quantumx-apps.com for any off-github topics. If its related to a specific application or repo such as this filebrowser, please open an issue or pull request instead. Email is only for corrospondance unrelated to technical changes or issues.
|
||||
|
||||
## Can I fork this repo and use it?
|
||||
|
||||
This repo has the same license as the original filebrowser, apache-2.0. Feel free to use in any way that follows the license. I have no issues with anything personally -- its open source please do as you like. However, since this is a fork of the OG repo, I am not sure what the consequences are for a fork of this repo.
|
||||
|
||||
## Who are the maintainers for FileBrowser Quantum?
|
||||
|
||||
Right now, just me as a personal hobby and some small contributions from the community. Once I can release a confident stable version, I plan to publicize this application more on social media. Hopefully, in the future I could pick up some extra contributors.
|
||||
|
||||
I'm not looking for contributors at the moment, but if you want to me a contributor feel free to email me at info@quantumx-apps.com to see about getting contributor access.
|
||||
|
||||
## Are there plans to charge for this product?
|
||||
|
||||
No, this repo and project will always be free to use.
|
||||
|
||||
## Is there a discord for this fork?
|
||||
|
||||
Not yet, generally most interactions should happen on github for now.
|
||||
|
|
@ -1,22 +1,46 @@
|
|||
# Planned Roadmap
|
||||
|
||||
upcoming 0.3.x releases, ordered by priority:
|
||||
- more indexing flexability
|
||||
- option not to index hidden files/folders
|
||||
- options folders to include/exclude from indexing
|
||||
- implement more indexing runners for more efficienct filesystem watching
|
||||
- more filetype previews: eg. raw img, office, photoshop, vector, 3d files.
|
||||
- introduce jobs as replacement to runners.
|
||||
Upcoming 0.3.x releases, ordered by priority:
|
||||
- Bring Themes and Branding back.
|
||||
- openoffice support https://github.com/filebrowser/filebrowser/pull/2954
|
||||
- More filetype previews: eg. raw img, office, photoshop, vector, 3d files.
|
||||
- Introduce jobs as replacement to runners.
|
||||
- Add Job status to the sidebar
|
||||
- index status.
|
||||
- Job status from users
|
||||
- upload status
|
||||
- opentelemetry metrics
|
||||
- Opentelemetry metrics
|
||||
- user access,
|
||||
- file access
|
||||
- download count
|
||||
- last login
|
||||
- more sign in support
|
||||
- LDAP
|
||||
- 2FA
|
||||
- SSO
|
||||
|
||||
Upcoming 0.4.x release:
|
||||
- Support for multiple filesystem sources https://github.com/filebrowser/filebrowser/issues/2514
|
||||
- Onboarding process to add sources and configure them on first run.
|
||||
- More indexing flexability
|
||||
- option not to index hidden files/folders
|
||||
- options folders to include/exclude from indexing
|
||||
- implement more indexing runners for more efficienct filesystem watching
|
||||
- tags support
|
||||
|
||||
Stable release (v1.0.0) - Planned 2025:
|
||||
- Once under the hood changes for things like multiple sources, jobs support, etc
|
||||
- More robust backend and frontend testing
|
||||
- Currently a stable release does not exist primarily because things are still changing, configuration changes are happening frequently and will for the next
|
||||
- Rebrand to QuantumX App suite umbrella branding and github repo change.
|
||||
|
||||
Unplanned Future releases:
|
||||
- multiple sources https://github.com/filebrowser/filebrowser/issues/2514
|
||||
- Add tools to sidebar
|
||||
- duplicate file detector.
|
||||
- bulk rename https://github.com/filebrowser/filebrowser/issues/2473
|
||||
- metrics tracker - user access, file access, download count, last login, etc
|
||||
- support minio, s3, and backblaze sources https://github.com/filebrowser/filebrowser/issues/2544
|
||||
- support more source types such as minio, s3, and backblaze sources https://github.com/filebrowser/filebrowser/issues/2544
|
||||
- Activity Log
|
||||
- Comments support
|
||||
- Trash Support
|
||||
- starred/pinned files
|
||||
- event based notifications
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
@ -19,7 +20,7 @@
|
|||
<!-- Add to home screen for Safari on iOS/iPadOS -->
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="assets">
|
||||
<link rel="apple-touch-icon" href="{{ .StaticURL }}/img/icons/apple-touch-icon.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ .StaticURL }}/img/icons/android-chrome-256x256.png">
|
||||
|
||||
<!-- Add to home screen for Windows -->
|
||||
<meta name="msapplication-TileImage" content="{{ .StaticURL }}/img/icons/mstile-144x144.png">
|
||||
|
@ -28,26 +29,29 @@
|
|||
<!-- Inject Some Variables and generate the manifest json -->
|
||||
<script>
|
||||
window.FileBrowser = JSON.parse('{{ .globalVars }}');
|
||||
|
||||
var fullStaticURL = window.location.origin + window.FileBrowser.StaticURL;
|
||||
var dynamicManifest = {
|
||||
"name": window.FileBrowser.Name || 'FileBrowser Quantum',
|
||||
"short_name": window.FileBrowser.Name || 'FileBrowser',
|
||||
"icons": [
|
||||
{
|
||||
"src": fullStaticURL + "/img/icons/android-chrome-256x256.png",
|
||||
"src": window.location.origin + "{{ .StaticURL }}/img/icons/android-chrome-256x256.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"src": window.location.origin + "{{ .StaticURL }}/img/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
],
|
||||
"start_url": fullStaticURL,
|
||||
"start_url": window.location.origin + "{{ .BaseURL }}",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": window.FileBrowser.Color || "#455a64"
|
||||
}
|
||||
|
||||
const stringManifest = JSON.stringify(dynamicManifest);
|
||||
const blob = new Blob([stringManifest], {type: 'application/json'});
|
||||
const blob = new Blob([stringManifest], { type: 'application/json' });
|
||||
const manifestURL = URL.createObjectURL(blob);
|
||||
document.querySelector('#manifestPlaceholder').setAttribute('href', manifestURL);
|
||||
</script>
|
||||
|
@ -79,7 +83,7 @@
|
|||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#loading .spinner > div {
|
||||
#loading .spinner>div {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #333;
|
||||
|
@ -100,21 +104,35 @@
|
|||
}
|
||||
|
||||
@-webkit-keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% { -webkit-transform: scale(0) }
|
||||
40% { -webkit-transform: scale(1.0) }
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0)
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%, 80%, 100% {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
-webkit-transform: scale(0);
|
||||
transform: scale(0);
|
||||
} 40% {
|
||||
}
|
||||
|
||||
40% {
|
||||
-webkit-transform: scale(1.0);
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
|
@ -138,7 +156,8 @@
|
|||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
{{ if .CSS }}
|
||||
<link rel="stylesheet" href="{{ .StaticURL }}/custom.css" >
|
||||
<link rel="stylesheet" href="{{ .StaticURL }}/custom.css">
|
||||
{{ end }}
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "FileBrowser",
|
||||
"short_name": "FileBrowser",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./img/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./static/img/icons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#455a64"
|
||||
}
|
|
@ -201,6 +201,7 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#context-menu.centered {
|
||||
|
|
|
@ -1,24 +1,9 @@
|
|||
<template>
|
||||
<div
|
||||
class="image-ex-container"
|
||||
ref="container"
|
||||
@touchstart="touchStart"
|
||||
@touchmove="touchMove"
|
||||
@dblclick="zoomAuto"
|
||||
@mousedown="mousedownStart"
|
||||
@mousemove="mouseMove"
|
||||
@mouseup="mouseUp"
|
||||
@wheel="wheelMove"
|
||||
>
|
||||
<div class="image-ex-container" ref="container" @touchstart="touchStart" @touchmove="touchMove" @dblclick="zoomAuto"
|
||||
@mousedown="mousedownStart" @mousemove="mouseMove" @mouseup="mouseUp" @wheel="wheelMove">
|
||||
<div v-if="!isLoaded">Loading image...</div>
|
||||
|
||||
<img
|
||||
v-if="!isTiff && isLoaded"
|
||||
:src="src"
|
||||
class="image-ex-img"
|
||||
ref="imgex"
|
||||
@load="onLoad"
|
||||
/>
|
||||
<img v-if="!isTiff && isLoaded" :src="src" class="image-ex-img" ref="imgex" @load="onLoad" />
|
||||
<canvas v-else-if="isLoaded" ref="imgex" class="image-ex-img"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -92,7 +77,7 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
src: function () {
|
||||
if (this.src == undefined || this.$refs.imgex == undefined) {
|
||||
if (!this.src || !this.$refs.imgex) {
|
||||
mutations.setLoading("preview-img", false);
|
||||
return;
|
||||
}
|
||||
|
@ -102,15 +87,18 @@ export default {
|
|||
} else {
|
||||
this.$refs.imgex.src = this.src;
|
||||
}
|
||||
|
||||
this.scale = 1;
|
||||
this.setZoom();
|
||||
this.setCenter();
|
||||
mutations.setLoading("preview-img", false);
|
||||
this.showSpinner = false;
|
||||
this.scale = 1; // Reset zoom level
|
||||
this.position.relative = { x: 0, y: 0 }; // Reset position
|
||||
this.showSpinner = true; // Show spinner while loading
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onLoad() {
|
||||
this.imageLoaded = true;
|
||||
this.setCenter(); // Center the image after loading
|
||||
this.showSpinner = false;
|
||||
mutations.setLoading("preview-img", false);
|
||||
},
|
||||
checkIfTiff(src) {
|
||||
const sufs = ["tif", "tiff", "dng", "cr2", "nef"];
|
||||
const suff = src.split(".").pop().toLowerCase();
|
||||
|
@ -144,16 +132,18 @@ export default {
|
|||
}
|
||||
}, 100),
|
||||
setCenter() {
|
||||
let container = this.$refs.container;
|
||||
let img = this.$refs.imgex;
|
||||
const container = this.$refs.container;
|
||||
const img = this.$refs.imgex;
|
||||
|
||||
if (!container || !img || !img.clientWidth || !img.clientHeight) {
|
||||
return; // Exit if dimensions are unavailable
|
||||
}
|
||||
|
||||
this.position.center.x = Math.floor((container.clientWidth - img.clientWidth) / 2);
|
||||
this.position.center.y = Math.floor(
|
||||
(container.clientHeight - img.clientHeight) / 2
|
||||
);
|
||||
this.position.center.y = Math.floor((container.clientHeight - img.clientHeight) / 2);
|
||||
|
||||
img.style.left = this.position.center.x + "px";
|
||||
img.style.top = this.position.center.y + "px";
|
||||
img.style.left = `${this.position.center.x}px`;
|
||||
img.style.top = `${this.position.center.y}px`;
|
||||
},
|
||||
mousedownStart(event) {
|
||||
this.lastX = null;
|
||||
|
@ -247,11 +237,13 @@ export default {
|
|||
this.$refs.imgex.style.transform = `translate(${this.position.relative.x}px, ${this.position.relative.y}px) scale(${this.scale})`;
|
||||
},
|
||||
wheelMove(event) {
|
||||
event.preventDefault()
|
||||
this.scale += -Math.sign(event.deltaY) * this.zoomStep;
|
||||
this.setZoom();
|
||||
},
|
||||
setZoom() {
|
||||
this.scale = Math.max(this.minScale, Math.min(this.maxScale, this.scale));
|
||||
|
||||
// Update the transform with both translate and scale values
|
||||
this.$refs.imgex.style.transform = `translate(${this.position.relative.x}px, ${this.position.relative.y}px) scale(${this.scale})`;
|
||||
},
|
||||
|
@ -264,17 +256,23 @@ export default {
|
|||
|
||||
<style>
|
||||
.image-ex-container {
|
||||
max-width: 100%; /* Image container max width */
|
||||
max-height: 100%; /* Image container max height */
|
||||
overflow: hidden; /* Hide overflow if image exceeds container */
|
||||
position: relative; /* Required for absolute positioning of child */
|
||||
max-width: 100%;
|
||||
/* Image container max width */
|
||||
max-height: 100%;
|
||||
/* Image container max height */
|
||||
overflow: hidden;
|
||||
/* Hide overflow if image exceeds container */
|
||||
position: relative;
|
||||
/* Required for absolute positioning of child */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-ex-img {
|
||||
max-width: 100%; /* Image max width */
|
||||
max-height: 100%; /* Image max height */
|
||||
max-width: 100%;
|
||||
/* Image max width */
|
||||
max-height: 100%;
|
||||
/* Image max height */
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
:data-type="type"
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected"
|
||||
@contextmenu="onRightClick"
|
||||
@contextmenu="onRightClick($event)"
|
||||
@click="click($event)"
|
||||
@touchstart="addSelected($event)"
|
||||
@touchmove="handleTouchMove($event)"
|
||||
@touchend="cancelContext($event)"
|
||||
@mouseup="cancelContext($event)"
|
||||
>
|
||||
<div @click="toggleClick" :class="{ activetitle: isMaximized && isSelected }">
|
||||
<img
|
||||
|
@ -91,6 +95,10 @@ export default {
|
|||
isThumbnailInView: false,
|
||||
isMaximized: false,
|
||||
touches: 0,
|
||||
touchStartX: 0,
|
||||
touchStartY: 0,
|
||||
isLongPress: false,
|
||||
isSwipe: false,
|
||||
};
|
||||
},
|
||||
props: [
|
||||
|
@ -169,6 +177,30 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
handleTouchMove(event) {
|
||||
if (!state.isSafari) return
|
||||
const touch = event.touches[0];
|
||||
const deltaX = Math.abs(touch.clientX - this.touchStartX);
|
||||
const deltaY = Math.abs(touch.clientY - this.touchStartY);
|
||||
// Set a threshold for movement to detect a swipe
|
||||
const movementThreshold = 10; // Adjust as needed
|
||||
if (deltaX > movementThreshold || deltaY > movementThreshold) {
|
||||
this.isSwipe = true;
|
||||
this.cancelContext(); // Cancel long press if swipe is detected
|
||||
}
|
||||
},
|
||||
handleTouchEnd() {
|
||||
if (!state.isSafari) return
|
||||
this.cancelContext(); // Clear timeout
|
||||
this.isSwipe = false; // Reset swipe state
|
||||
},
|
||||
cancelContext() {
|
||||
if (this.contextTimeout) {
|
||||
clearTimeout(this.contextTimeout);
|
||||
this.contextTimeout = null;
|
||||
}
|
||||
this.isLongPress = false;
|
||||
},
|
||||
updateHashAndNavigate(path) {
|
||||
// Update hash in the browser without full page reload
|
||||
window.location.hash = path;
|
||||
|
@ -181,7 +213,6 @@ export default {
|
|||
},
|
||||
onRightClick(event) {
|
||||
event.preventDefault(); // Prevent default context menu
|
||||
|
||||
// If no items are selected, select the right-clicked item
|
||||
if (!state.multiple) {
|
||||
mutations.resetSelected();
|
||||
|
@ -296,6 +327,22 @@ export default {
|
|||
|
||||
action(overwrite, rename);
|
||||
},
|
||||
addSelected(event) {
|
||||
if (!state.isSafari) return
|
||||
const touch = event.touches[0];
|
||||
this.touchStartX = touch.clientX;
|
||||
this.touchStartY = touch.clientY;
|
||||
this.isLongPress = false; // Reset state
|
||||
this.isSwipe = false; // Reset swipe detection
|
||||
if (!state.multiple) {
|
||||
this.contextTimeout = setTimeout(() => {
|
||||
if (!this.isSwipe) {
|
||||
mutations.resetSelected();
|
||||
mutations.addSelected(this.index);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
click(event) {
|
||||
if (event.button === 0) {
|
||||
// Left-click
|
||||
|
@ -305,13 +352,12 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
if (!this.singleClick && getters.selectedCount() !== 0 && event.button === 0) {
|
||||
if (!state.user.singleClick && getters.selectedCount() !== 0 && event.button === 0) {
|
||||
event.preventDefault();
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.touches = 0;
|
||||
}, 500);
|
||||
|
||||
this.touches++;
|
||||
if (this.touches > 1) {
|
||||
this.open();
|
||||
|
@ -342,7 +388,7 @@ export default {
|
|||
|
||||
return;
|
||||
}
|
||||
if (!this.singleClick && !event.ctrlKey && !event.metaKey && !state.multiple) {
|
||||
if (!state.user.singleClick && !event.ctrlKey && !event.metaKey && !state.multiple) {
|
||||
mutations.resetSelected();
|
||||
}
|
||||
mutations.addSelected(this.index);
|
||||
|
@ -355,3 +401,10 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.item {
|
||||
-webkit-touch-callout: none; /* Disable the default long press preview */
|
||||
user-select: none; /* Optional: Disable text selection for better UX */
|
||||
}
|
||||
</style>
|
|
@ -112,7 +112,7 @@ export default {
|
|||
}
|
||||
action(overwrite, rename);
|
||||
} catch (e) {
|
||||
notify.error(e);
|
||||
notify.showError(e);
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
--item-selected: white;
|
||||
transition: all;
|
||||
animation-duration: 0.25s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body.rtl #listingView {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { reactive } from 'vue';
|
|||
import { detectLocale } from "@/i18n";
|
||||
|
||||
export const state = reactive({
|
||||
isSafari: /^((?!chrome|android).)*safari/i.test(navigator.userAgent),
|
||||
activeSettingsView: "",
|
||||
isMobile: window.innerWidth <= 800,
|
||||
showSidebar: false,
|
||||
|
@ -13,6 +14,7 @@ export const state = reactive({
|
|||
editor: null,
|
||||
user: {
|
||||
gallarySize: 0,
|
||||
singleClick: false,
|
||||
stickySidebar: stickyStartup(),
|
||||
locale: detectLocale(), // Default to the locale from moment
|
||||
viewMode: 'normal', // Default to mosaic view
|
||||
|
|
|
@ -16,64 +16,26 @@
|
|||
<i class="material-icons">sentiment_dissatisfied</i>
|
||||
<span>{{ $t("files.lonely") }}</span>
|
||||
</h2>
|
||||
<input
|
||||
style="display: none"
|
||||
type="file"
|
||||
id="upload-input"
|
||||
@change="uploadInput($event)"
|
||||
multiple
|
||||
/>
|
||||
<input
|
||||
style="display: none"
|
||||
type="file"
|
||||
id="upload-folder-input"
|
||||
@change="uploadInput($event)"
|
||||
webkitdirectory
|
||||
multiple
|
||||
/>
|
||||
<input style="display: none" type="file" id="upload-input" @change="uploadInput($event)" multiple />
|
||||
<input style="display: none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory
|
||||
multiple />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
id="listingView"
|
||||
ref="listingView"
|
||||
:class="listingViewMode + ' file-icons'"
|
||||
>
|
||||
<div v-else id="listingView" ref="listingView" :class="listingViewMode + ' file-icons'">
|
||||
<div>
|
||||
<div class="header" :class="{ 'dark-mode-item-header': isDarkMode }">
|
||||
<p
|
||||
:class="{ active: nameSorted }"
|
||||
class="name"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('name')"
|
||||
:title="$t('files.sortByName')"
|
||||
:aria-label="$t('files.sortByName')"
|
||||
>
|
||||
<p :class="{ active: nameSorted }" class="name" role="button" tabindex="0" @click="sort('name')"
|
||||
:title="$t('files.sortByName')" :aria-label="$t('files.sortByName')">
|
||||
<span>{{ $t("files.name") }}</span>
|
||||
<i class="material-icons">{{ nameIcon }}</i>
|
||||
</p>
|
||||
|
||||
<p
|
||||
:class="{ active: sizeSorted }"
|
||||
class="size"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('size')"
|
||||
:title="$t('files.sortBySize')"
|
||||
:aria-label="$t('files.sortBySize')"
|
||||
>
|
||||
<p :class="{ active: sizeSorted }" class="size" role="button" tabindex="0" @click="sort('size')"
|
||||
:title="$t('files.sortBySize')" :aria-label="$t('files.sortBySize')">
|
||||
<span>{{ $t("files.size") }}</span>
|
||||
<i class="material-icons">{{ sizeIcon }}</i>
|
||||
</p>
|
||||
<p
|
||||
:class="{ active: modifiedSorted }"
|
||||
class="modified"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click="sort('modified')"
|
||||
:title="$t('files.sortByLastModified')"
|
||||
:aria-label="$t('files.sortByLastModified')"
|
||||
>
|
||||
<p :class="{ active: modifiedSorted }" class="modified" role="button" tabindex="0" @click="sort('modified')"
|
||||
:title="$t('files.sortByLastModified')" :aria-label="$t('files.sortByLastModified')">
|
||||
<span>{{ $t("files.lastModified") }}</span>
|
||||
<i class="material-icons">{{ modifiedIcon }}</i>
|
||||
</p>
|
||||
|
@ -84,23 +46,10 @@
|
|||
<h2>{{ $t("files.folders") }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="numDirs > 0"
|
||||
class="folder-items"
|
||||
:class="{ lastGroup: numFiles === 0 }"
|
||||
>
|
||||
<item
|
||||
v-for="item in dirs"
|
||||
:key="base64(item.name)"
|
||||
v-bind:index="item.index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.type == 'directory'"
|
||||
v-bind:url="item.url"
|
||||
v-bind:modified="item.modified"
|
||||
v-bind:type="item.type"
|
||||
v-bind:size="item.size"
|
||||
v-bind:path="item.path"
|
||||
/>
|
||||
<div v-if="numDirs > 0" class="folder-items" :class="{ lastGroup: numFiles === 0 }">
|
||||
<item v-for="item in dirs" :key="base64(item.name)" v-bind:index="item.index" v-bind:name="item.name"
|
||||
v-bind:isDir="item.type == 'directory'" v-bind:url="item.url" v-bind:modified="item.modified"
|
||||
v-bind:type="item.type" v-bind:size="item.size" v-bind:path="item.path" />
|
||||
</div>
|
||||
<div v-if="numFiles > 0">
|
||||
<div class="header-items">
|
||||
|
@ -108,35 +57,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="numFiles > 0" class="file-items" :class="{ lastGroup: numFiles > 0 }">
|
||||
<item
|
||||
v-for="item in files"
|
||||
:key="base64(item.name)"
|
||||
v-bind:index="item.index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.type == 'directory'"
|
||||
v-bind:url="item.url"
|
||||
v-bind:modified="item.modified"
|
||||
v-bind:type="item.type"
|
||||
v-bind:size="item.size"
|
||||
v-bind:path="item.path"
|
||||
/>
|
||||
<item v-for="item in files" :key="base64(item.name)" v-bind:index="item.index" v-bind:name="item.name"
|
||||
v-bind:isDir="item.type == 'directory'" v-bind:url="item.url" v-bind:modified="item.modified"
|
||||
v-bind:type="item.type" v-bind:size="item.size" v-bind:path="item.path" />
|
||||
</div>
|
||||
|
||||
<input
|
||||
style="display: none"
|
||||
type="file"
|
||||
id="upload-input"
|
||||
@change="uploadInput($event)"
|
||||
multiple
|
||||
/>
|
||||
<input
|
||||
style="display: none"
|
||||
type="file"
|
||||
id="upload-folder-input"
|
||||
@change="uploadInput($event)"
|
||||
webkitdirectory
|
||||
multiple
|
||||
/>
|
||||
<input style="display: none" type="file" id="upload-input" @change="uploadInput($event)" multiple />
|
||||
<input style="display: none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory
|
||||
multiple />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -290,14 +218,12 @@ export default {
|
|||
window.addEventListener("resize", this.windowsResize);
|
||||
this.$el.addEventListener("click", this.clickClear);
|
||||
|
||||
// Detect Safari
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
|
||||
// Adjust contextmenu listener based on browser
|
||||
if (isSafari) {
|
||||
if (state.isSafari) {
|
||||
// For Safari, add touchstart or mousedown to open the context menu
|
||||
this.$el.addEventListener("touchstart", this.openContextForSafari);
|
||||
this.$el.addEventListener("mousedown", this.openContextForSafari);
|
||||
this.$el.addEventListener("touchmove", this.handleTouchMove);
|
||||
|
||||
// Also clear the timeout if the user clicks or taps quickly
|
||||
this.$el.addEventListener("touchend", this.cancelContext);
|
||||
|
@ -319,29 +245,57 @@ export default {
|
|||
window.removeEventListener("scroll", this.scrollEvent);
|
||||
window.removeEventListener("resize", this.windowsResize);
|
||||
// If Safari, remove touchstart listener
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||
if (isSafari) {
|
||||
if (state.isSafari) {
|
||||
this.$el.removeEventListener("touchstart", this.openContextForSafari);
|
||||
this.$el.removeEventListener("mousedown", this.openContextForSafari);
|
||||
this.$el.removeEventListener("touchend", this.cancelContext);
|
||||
this.$el.removeEventListener("mouseup", this.cancelContext);
|
||||
this.$el.removeEventListener("touchmove", this.handleTouchMove);
|
||||
|
||||
} else {
|
||||
window.removeEventListener("contextmenu", this.openContext);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancelContext(event) {
|
||||
cancelContext() {
|
||||
if (this.contextTimeout) {
|
||||
clearTimeout(this.contextTimeout);
|
||||
this.contextTimeout = null;
|
||||
}
|
||||
this.isLongPress = false;
|
||||
},
|
||||
openContextForSafari(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
// Set a timeout that triggers after 500ms of hold
|
||||
this.cancelContext(); // Clear any previous timeouts
|
||||
this.isLongPress = false; // Reset state
|
||||
this.isSwipe = false; // Reset swipe detection
|
||||
|
||||
const touch = event.touches[0];
|
||||
this.touchStartX = touch.clientX;
|
||||
this.touchStartY = touch.clientY;
|
||||
|
||||
// Start the long press detection
|
||||
this.contextTimeout = setTimeout(() => {
|
||||
this.openContext(event);
|
||||
}, 500); // You can adjust the delay (500ms) to mimic "click and hold"
|
||||
if (!this.isSwipe) {
|
||||
this.isLongPress = true;
|
||||
event.preventDefault(); // Suppress Safari's callout menu
|
||||
this.openContext(event); // Open the custom context menu
|
||||
}
|
||||
}, 500); // Long press delay (adjust as needed)
|
||||
},
|
||||
handleTouchMove(event) {
|
||||
const touch = event.touches[0];
|
||||
const deltaX = Math.abs(touch.clientX - this.touchStartX);
|
||||
const deltaY = Math.abs(touch.clientY - this.touchStartY);
|
||||
// Set a threshold for movement to detect a swipe
|
||||
const movementThreshold = 10; // Adjust as needed
|
||||
if (deltaX > movementThreshold || deltaY > movementThreshold) {
|
||||
this.isSwipe = true;
|
||||
this.cancelContext(); // Cancel long press if swipe is detected
|
||||
}
|
||||
},
|
||||
handleTouchEnd() {
|
||||
this.cancelContext(); // Clear timeout
|
||||
this.isSwipe = false; // Reset swipe state
|
||||
},
|
||||
base64(name) {
|
||||
return url.base64Encode(name);
|
||||
|
@ -354,7 +308,6 @@ export default {
|
|||
mutations.addSelected(allItems[0].index);
|
||||
}
|
||||
},
|
||||
|
||||
// Helper method to select an item by index
|
||||
selectItem(index) {
|
||||
mutations.resetSelected();
|
||||
|
@ -877,6 +830,7 @@ export default {
|
|||
.dark-mode-item-header {
|
||||
border-color: var(--divider) !important;
|
||||
background: var(--surfacePrimary) !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header-items {
|
||||
|
|
|
@ -3,39 +3,17 @@
|
|||
<div class="preview">
|
||||
<ExtendedImage v-if="getSimpleType(currentItem.type) == 'image'" :src="raw">
|
||||
</ExtendedImage>
|
||||
<audio
|
||||
v-else-if="getSimpleType(currentItem.type) == 'audio'"
|
||||
ref="player"
|
||||
:src="raw"
|
||||
controls
|
||||
:autoplay="autoPlay"
|
||||
@play="autoPlay = true"
|
||||
></audio>
|
||||
<video
|
||||
v-else-if="getSimpleType(currentItem.type) == 'video'"
|
||||
ref="player"
|
||||
:src="raw"
|
||||
controls
|
||||
:autoplay="autoPlay"
|
||||
@play="autoPlay = true"
|
||||
>
|
||||
<track
|
||||
kind="captions"
|
||||
v-for="(sub, index) in subtitles"
|
||||
:key="index"
|
||||
:src="sub"
|
||||
:label="'Subtitle ' + index"
|
||||
:default="index === 0"
|
||||
/>
|
||||
<audio v-else-if="getSimpleType(currentItem.type) == 'audio'" ref="player" :src="raw" controls
|
||||
:autoplay="autoPlay" @play="autoPlay = true"></audio>
|
||||
<video v-else-if="getSimpleType(currentItem.type) == 'video'" ref="player" :src="raw" controls
|
||||
:autoplay="autoPlay" @play="autoPlay = true">
|
||||
<track kind="captions" v-for="(sub, index) in subtitles" :key="index" :src="sub" :label="'Subtitle ' + index"
|
||||
:default="index === 0" />
|
||||
Sorry, your browser doesn't support embedded videos, but don't worry, you can
|
||||
<a :href="downloadUrl">download it</a>
|
||||
and watch it with your favorite video player!
|
||||
</video>
|
||||
<object
|
||||
v-else-if="getSimpleType(currentItem.type) == 'pdf'"
|
||||
class="pdf"
|
||||
:data="raw"
|
||||
></object>
|
||||
<object v-else-if="getSimpleType(currentItem.type) == 'pdf'" class="pdf" :data="raw"></object>
|
||||
<div v-else class="info">
|
||||
<div class="title">
|
||||
<i class="material-icons">feedback</i>
|
||||
|
@ -47,12 +25,7 @@
|
|||
<i class="material-icons">file_download</i>{{ $t("buttons.download") }}
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
:href="raw"
|
||||
class="button button--flat"
|
||||
v-if="currentItem.type != 'directory'"
|
||||
>
|
||||
<a target="_blank" :href="raw" class="button button--flat" v-if="currentItem.type != 'directory'">
|
||||
<div>
|
||||
<i class="material-icons">open_in_new</i>{{ $t("buttons.openFile") }}
|
||||
</div>
|
||||
|
@ -61,24 +34,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click="prev"
|
||||
@mouseover="hoverNav = true"
|
||||
@mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasPrevious || !showNav }"
|
||||
:aria-label="$t('buttons.previous')"
|
||||
:title="$t('buttons.previous')"
|
||||
>
|
||||
<button @click="prev" @mouseover="hoverNav = true" @mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasPrevious || !showNav }" :aria-label="$t('buttons.previous')"
|
||||
:title="$t('buttons.previous')">
|
||||
<i class="material-icons">chevron_left</i>
|
||||
</button>
|
||||
<button
|
||||
@click="next"
|
||||
@mouseover="hoverNav = true"
|
||||
@mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasNext || !showNav }"
|
||||
:aria-label="$t('buttons.next')"
|
||||
:title="$t('buttons.next')"
|
||||
>
|
||||
<button @click="next" @mouseover="hoverNav = true" @mouseleave="hoverNav = false"
|
||||
:class="{ hidden: !hasNext || !showNav }" :aria-label="$t('buttons.next')" :title="$t('buttons.next')">
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</button>
|
||||
<link rel="prefetch" :href="previousRaw" />
|
||||
|
|
|
@ -23,8 +23,6 @@ const resolve = {
|
|||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ command }) => {
|
||||
|
||||
// command === 'build'
|
||||
return {
|
||||
plugins,
|
||||
resolve,
|
||||
|
@ -56,11 +54,10 @@ export default defineConfig(({ command }) => {
|
|||
},
|
||||
test: {
|
||||
globals: true,
|
||||
include: ["src/**/*.test.js"], // Explicitly include test files only
|
||||
exclude: ["src/**/*.vue"], // Exclude Vue files unless tested directly
|
||||
environment: "jsdom", // jsdom environment
|
||||
setupFiles: "tests/mocks/setup.js", // Setup file for tests
|
||||
include: ["src/**/*.test.js"],
|
||||
exclude: ["src/**/*.vue"],
|
||||
environment: "jsdom",
|
||||
setupFiles: "tests/mocks/setup.js",
|
||||
},
|
||||
};
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue