lightly-studio 0.3.4__py3-none-any.whl → 0.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lightly-studio might be problematic. Click here for more details.
- lightly_studio/__init__.py +1 -2
- lightly_studio/api/db_tables.py +3 -0
- lightly_studio/api/features.py +3 -5
- lightly_studio/api/routes/api/dataset.py +5 -3
- lightly_studio/api/routes/api/dataset_tag.py +10 -0
- lightly_studio/api/routes/api/embeddings2d.py +6 -47
- lightly_studio/core/dataset.py +89 -2
- lightly_studio/core/dataset_query/__init__.py +14 -0
- lightly_studio/core/dataset_query/dataset_query.py +98 -2
- lightly_studio/core/dataset_query/sample_field.py +11 -2
- lightly_studio/core/sample.py +33 -1
- lightly_studio/db_manager.py +4 -2
- lightly_studio/dist_lightly_studio_view_app/_app/env.js +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.B-Bn8V7R.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/Samples.CTl60pQb.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.CxuTMeYy.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/transform.Bte-FZ4V.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/7XnHnNk_.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BDE1kT04.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DQ8aZ1o-.js → BHoO7UL_.js} +2 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{CZGpyrcA.js → BTpMiPuk.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BbG6fgnL.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bip0vTKl.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{D_JuJOO3.js → BzYRexN9.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Frwd2CjB.js → C3xJX0nD.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/C5oezL0m.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{H4l0JFh9.js → CANX9QXL.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CAPx0Bfm.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CEPpkZIV.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/ChO13USc.js +96 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DzBTnFhV.js → Cj4nZbtb.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Bqz7dyEC.js → CmNXvs5e.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CpbA3HU7.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DSxvnAMh.js → D9WxTSWa.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DELrRF6e.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{sLqs1uaK.js → DFib3GIP.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{CSCQddQS.js → DNJnBfHs.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DVxjPOJB.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DeAqvaPB.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DgCXCApo.js +438 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DmGM9V9Q.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DoEId1MK.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DqhovIS0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/SiegjVo0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/gBp1tBnA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/xQhUoIl9.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.CED-eiXl.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.D6UcpqyO.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.waSqEGqm.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{1.Cdy-7S5q.js → 1.Bb_sKkYl.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.D9Uicmfq.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/11.DKnK0EcJ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/13.D7dGgxYC.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{2.BIldfkxL.js → 2.BzK1sjoM.js} +144 -105
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{3.BC9z_TWM.js → 3.B08UPFst.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{4.D8X_Ch5n.js → 4.C19_Nx95.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.B2cR7brs.js +39 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{6.DRA5Ru_2.js → 6.C9Y2LZxG.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{7.WVBsruHQ.js → 7.CEbWmgIF.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Con-GcPz.js +20 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.DW7FBNNH.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -1
- lightly_studio/dist_lightly_studio_view_app/index.html +13 -14
- lightly_studio/models/annotation/annotation_base.py +9 -0
- lightly_studio/models/caption.py +1 -0
- lightly_studio/models/dataset.py +6 -0
- lightly_studio/models/sample.py +8 -1
- lightly_studio/models/settings.py +5 -0
- lightly_studio/models/two_dim_embedding.py +16 -0
- lightly_studio/resolvers/annotation_resolver/get_all.py +3 -1
- lightly_studio/resolvers/dataset_resolver.py +20 -5
- lightly_studio/resolvers/sample_embedding_resolver.py +44 -2
- lightly_studio/resolvers/sample_resolver.py +2 -3
- lightly_studio/resolvers/settings_resolver.py +3 -0
- lightly_studio/resolvers/twodim_embedding_resolver.py +119 -0
- lightly_studio/selection/__init__.py +1 -0
- lightly_studio/selection/mundig.py +41 -0
- lightly_studio/selection/select.py +70 -1
- lightly_studio/selection/selection_config.py +1 -1
- lightly_studio-0.4.1.dist-info/METADATA +78 -0
- {lightly_studio-0.3.4.dist-info → lightly_studio-0.4.1.dist-info}/RECORD +83 -81
- lightly_studio/dataset/loader.py +0 -581
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.B3oFNb6O.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/Samples.CIbricz7.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.7Ma7YdVg.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/transform.2jKMtOWG.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/-DXuGN29.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/B7302SU7.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BeWf8-vJ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CfQ4mGwl.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CiaNZCBa.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cqo0Vpvt.js +0 -417
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cy4fgWTG.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D5w4xp5l.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DD63uD-T.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D_ynJAfY.js +0 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Dafy4oEQ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Dj4O-5se.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DmjAI-UV.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Dug7Bq1S.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Dv5BSBQG.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DzX_yyqb.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/H60ATh8g.js +0 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/qIv1kPyv.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/u-it74zV.js +0 -96
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/app.BPc0HQPq.js +0 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.SNvc2nrm.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.5jT7P06o.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/10.C_uoESTX.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/12.DcO8wIAc.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.CAXhxJu6.js +0 -39
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.BuKUrCEN.js +0 -20
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/9.CUIn1yCR.js +0 -1
- lightly_studio/selection/README.md +0 -6
- lightly_studio-0.3.4.dist-info/METADATA +0 -879
- /lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{11.CWG1ehzT.js → 12.CWG1ehzT.js} +0 -0
- {lightly_studio-0.3.4.dist-info → lightly_studio-0.4.1.dist-info}/WHEEL +0 -0
lightly_studio/__init__.py
CHANGED
|
@@ -5,7 +5,6 @@ from lightly_studio.core.dataset import Dataset
|
|
|
5
5
|
from lightly_studio.core.start_gui import start_gui
|
|
6
6
|
|
|
7
7
|
# TODO (Jonas 08/25): This will be removed as soon as the new interface is used in the examples
|
|
8
|
-
from lightly_studio.dataset.loader import DatasetLoader
|
|
9
8
|
from lightly_studio.models.annotation.annotation_base import AnnotationType
|
|
10
9
|
|
|
11
|
-
__all__ = ["AnnotationType", "Dataset", "
|
|
10
|
+
__all__ = ["AnnotationType", "Dataset", "start_gui"]
|
lightly_studio/api/db_tables.py
CHANGED
|
@@ -27,3 +27,6 @@ from lightly_studio.models.settings import (
|
|
|
27
27
|
from lightly_studio.models.tag import (
|
|
28
28
|
TagTable, # noqa: F401, required for SQLModel to work properly
|
|
29
29
|
)
|
|
30
|
+
from lightly_studio.models.two_dim_embedding import (
|
|
31
|
+
TwoDimEmbeddingTable, # noqa: F401, required for SQLModel to work properly
|
|
32
|
+
)
|
lightly_studio/api/features.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
"""Global list
|
|
1
|
+
"""Global list of the active features."""
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
# https://linear.app/lightly/issue/LIG-6708/introduce-apifeatures-endpoint
|
|
5
|
-
from typing import List
|
|
3
|
+
from __future__ import annotations
|
|
6
4
|
|
|
7
|
-
lightly_studio_active_features:
|
|
5
|
+
lightly_studio_active_features: list[str] = []
|
|
@@ -19,6 +19,7 @@ from lightly_studio.models.dataset import (
|
|
|
19
19
|
DatasetCreate,
|
|
20
20
|
DatasetTable,
|
|
21
21
|
DatasetView,
|
|
22
|
+
DatasetViewWithCount,
|
|
22
23
|
)
|
|
23
24
|
from lightly_studio.resolvers import dataset_resolver
|
|
24
25
|
from lightly_studio.resolvers.dataset_resolver import (
|
|
@@ -64,16 +65,17 @@ def read_datasets(
|
|
|
64
65
|
return dataset_resolver.get_all(session=session, offset=paginated.offset, limit=paginated.limit)
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
@dataset_router.get("/datasets/{dataset_id}")
|
|
68
|
+
@dataset_router.get("/datasets/{dataset_id}", response_model=DatasetViewWithCount)
|
|
68
69
|
def read_dataset(
|
|
70
|
+
session: SessionDep,
|
|
69
71
|
dataset: Annotated[
|
|
70
72
|
DatasetTable,
|
|
71
73
|
Path(title="Dataset Id"),
|
|
72
74
|
Depends(get_and_validate_dataset_id),
|
|
73
75
|
],
|
|
74
|
-
) ->
|
|
76
|
+
) -> DatasetViewWithCount:
|
|
75
77
|
"""Retrieve a single dataset from the database."""
|
|
76
|
-
return dataset
|
|
78
|
+
return dataset_resolver.get_dataset_details(session=session, dataset=dataset)
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
@dataset_router.put("/datasets/{dataset_id}")
|
|
@@ -164,6 +164,11 @@ class SampleIdsBody(BaseModel):
|
|
|
164
164
|
)
|
|
165
165
|
def add_sample_ids_to_tag_id(
|
|
166
166
|
session: SessionDep,
|
|
167
|
+
# dataset_id is needed for the generator
|
|
168
|
+
dataset_id: Annotated[ # noqa: ARG001
|
|
169
|
+
UUID,
|
|
170
|
+
Path(title="Dataset Id", description="The ID of the dataset"),
|
|
171
|
+
],
|
|
167
172
|
tag_id: UUID,
|
|
168
173
|
body: SampleIdsBody,
|
|
169
174
|
) -> bool:
|
|
@@ -215,6 +220,11 @@ class AnnotationIdsBody(BaseModel):
|
|
|
215
220
|
)
|
|
216
221
|
def add_annotation_ids_to_tag_id(
|
|
217
222
|
session: SessionDep,
|
|
223
|
+
# dataset_id is needed for the generator
|
|
224
|
+
dataset_id: Annotated[ # noqa: ARG001
|
|
225
|
+
UUID,
|
|
226
|
+
Path(title="Dataset Id", description="The ID of the dataset"),
|
|
227
|
+
],
|
|
218
228
|
tag_id: UUID,
|
|
219
229
|
body: AnnotationIdsBody,
|
|
220
230
|
) -> bool:
|
|
@@ -5,19 +5,16 @@ from __future__ import annotations
|
|
|
5
5
|
import io
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
|
-
import numpy as np
|
|
9
8
|
import pyarrow as pa
|
|
10
9
|
from fastapi import APIRouter, HTTPException, Response
|
|
11
|
-
from numpy.typing import NDArray
|
|
12
10
|
from pyarrow import ipc
|
|
13
11
|
from pydantic import BaseModel, Field
|
|
14
|
-
from sklearn.manifold import TSNE
|
|
15
12
|
from sqlmodel import select
|
|
16
13
|
|
|
17
14
|
from lightly_studio.db_manager import SessionDep
|
|
18
15
|
from lightly_studio.models.dataset import DatasetTable
|
|
19
16
|
from lightly_studio.models.embedding_model import EmbeddingModelTable
|
|
20
|
-
from lightly_studio.resolvers import
|
|
17
|
+
from lightly_studio.resolvers import sample_resolver, twodim_embedding_resolver
|
|
21
18
|
from lightly_studio.resolvers.samples_filter import SampleFilter
|
|
22
19
|
|
|
23
20
|
embeddings2d_router = APIRouter()
|
|
@@ -32,8 +29,8 @@ class GetEmbeddings2DRequest(BaseModel):
|
|
|
32
29
|
)
|
|
33
30
|
|
|
34
31
|
|
|
35
|
-
@embeddings2d_router.post("/embeddings2d/
|
|
36
|
-
def
|
|
32
|
+
@embeddings2d_router.post("/embeddings2d/default")
|
|
33
|
+
def get_2d_embeddings(
|
|
37
34
|
session: SessionDep,
|
|
38
35
|
body: GetEmbeddings2DRequest | None = None,
|
|
39
36
|
) -> Response:
|
|
@@ -52,17 +49,12 @@ def get_embeddings2d__tsne(
|
|
|
52
49
|
if embedding_model is None:
|
|
53
50
|
raise HTTPException(status_code=404, detail="No embedding model configured.")
|
|
54
51
|
|
|
55
|
-
|
|
52
|
+
x_array, y_array, sample_ids = twodim_embedding_resolver.get_twodim_embeddings(
|
|
56
53
|
session=session,
|
|
57
54
|
dataset_id=dataset.dataset_id,
|
|
58
55
|
embedding_model_id=embedding_model.embedding_model_id,
|
|
59
56
|
)
|
|
60
57
|
|
|
61
|
-
embedding_values = np.asarray([e.embedding for e in embeddings], dtype=np.float32)
|
|
62
|
-
embedding_values_tsne = _calculate_tsne_embeddings(embedding_values)
|
|
63
|
-
x = embedding_values_tsne[:, 0]
|
|
64
|
-
y = embedding_values_tsne[:, 1]
|
|
65
|
-
|
|
66
58
|
matching_sample_ids: set[UUID] | None = None
|
|
67
59
|
filters = body.filters if body else None
|
|
68
60
|
if filters:
|
|
@@ -73,20 +65,15 @@ def get_embeddings2d__tsne(
|
|
|
73
65
|
)
|
|
74
66
|
matching_sample_ids = {sample.sample_id for sample in matching_samples_result.samples}
|
|
75
67
|
|
|
76
|
-
sample_ids = [embedding.sample_id for embedding in embeddings]
|
|
77
68
|
if matching_sample_ids is None:
|
|
78
69
|
fulfils_filter = [1] * len(sample_ids)
|
|
79
70
|
else:
|
|
80
71
|
fulfils_filter = [1 if sample_id in matching_sample_ids else 0 for sample_id in sample_ids]
|
|
81
72
|
|
|
82
|
-
# TODO(Malte, 09/2025): Save the 2D-embeddings in the database to avoid recomputing
|
|
83
|
-
# them on every request.
|
|
84
|
-
|
|
85
|
-
# TODO(Malte, 09/2025): Include a sample identifier in the returned payload.
|
|
86
73
|
table = pa.table(
|
|
87
74
|
{
|
|
88
|
-
"x": pa.array(
|
|
89
|
-
"y": pa.array(
|
|
75
|
+
"x": pa.array(x_array, type=pa.float32()),
|
|
76
|
+
"y": pa.array(y_array, type=pa.float32()),
|
|
90
77
|
"fulfils_filter": pa.array(fulfils_filter, type=pa.uint8()),
|
|
91
78
|
"sample_id": pa.array([str(sample_id) for sample_id in sample_ids], type=pa.string()),
|
|
92
79
|
}
|
|
@@ -106,31 +93,3 @@ def get_embeddings2d__tsne(
|
|
|
106
93
|
"X-Content-Type-Options": "nosniff",
|
|
107
94
|
},
|
|
108
95
|
)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def _calculate_tsne_embeddings(embedding_values: NDArray[np.float32]) -> NDArray[np.float32]:
|
|
112
|
-
# TODO(Malte, 10/2025): Switch to a better and faster projection method than
|
|
113
|
-
# scikit-learn's TSNE.
|
|
114
|
-
# See https://linear.app/lightly/issue/LIG-7678/embedding-plot-investigate-fasterandbetter-2d-computation-options
|
|
115
|
-
n_samples = embedding_values.shape[0]
|
|
116
|
-
# For 0, 1 or 2 samples we hard-code deterministic coordinates.
|
|
117
|
-
if n_samples == 0:
|
|
118
|
-
return np.zeros((0, 2), dtype=np.float32)
|
|
119
|
-
if n_samples == 1:
|
|
120
|
-
return np.asarray([[0.0, 0.0]], dtype=np.float32)
|
|
121
|
-
if n_samples == 2: # noqa: PLR2004
|
|
122
|
-
return np.asarray([[0.0, 0.0], [1.0, 1.0]], dtype=np.float32)
|
|
123
|
-
|
|
124
|
-
# Copied from lightly-core:
|
|
125
|
-
# https://github.com/lightly-ai/lightly-core/blob/b738952516e916eba42fdd28498491ff18df5c1e/appv2/packages/queueworker/src/jobs/embeddings2d/function-source/main.py#L179-L186
|
|
126
|
-
embeddings_2d: NDArray[np.float32] = TSNE(
|
|
127
|
-
init="pca", # changed in https://github.com/scikit-learn/scikit-learn/issues/18018
|
|
128
|
-
learning_rate="auto", # changed in https://github.com/scikit-learn/scikit-learn/issues/18018
|
|
129
|
-
n_components=2,
|
|
130
|
-
# Perplexity must be _less_ than the number of entries. 30 is the default value.
|
|
131
|
-
# https://scikit-learn.org/stable/modules/generated/sklearn.manifold.TSNE.html
|
|
132
|
-
perplexity=min(30.0, float(n_samples - 1)),
|
|
133
|
-
# Make the computation deterministic.
|
|
134
|
-
random_state=0,
|
|
135
|
-
).fit_transform(embedding_values)
|
|
136
|
-
return embeddings_2d
|
lightly_studio/core/dataset.py
CHANGED
|
@@ -38,6 +38,7 @@ from lightly_studio.models.sample import SampleTable
|
|
|
38
38
|
from lightly_studio.resolvers import (
|
|
39
39
|
dataset_resolver,
|
|
40
40
|
embedding_model_resolver,
|
|
41
|
+
sample_embedding_resolver,
|
|
41
42
|
sample_resolver,
|
|
42
43
|
tag_resolver,
|
|
43
44
|
)
|
|
@@ -53,7 +54,40 @@ _SliceType = slice # to avoid shadowing built-in slice in type annotations
|
|
|
53
54
|
class Dataset:
|
|
54
55
|
"""A LightlyStudio Dataset.
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
It can be created or loaded using one of the static methods:
|
|
58
|
+
```python
|
|
59
|
+
dataset = Dataset.create()
|
|
60
|
+
dataset = Dataset.load()
|
|
61
|
+
dataset = Dataset.load_or_create()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Samples can be added to the dataset using various methods:
|
|
65
|
+
```python
|
|
66
|
+
dataset.add_samples_from_path(...)
|
|
67
|
+
dataset.add_samples_from_yolo(...)
|
|
68
|
+
dataset.add_samples_from_coco(...)
|
|
69
|
+
dataset.add_samples_from_coco_caption(...)
|
|
70
|
+
dataset.add_samples_from_labelformat(...)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The dataset samples can be queried directly by iterating over it or slicing it:
|
|
74
|
+
```python
|
|
75
|
+
dataset = Dataset.load("my_dataset")
|
|
76
|
+
first_ten_samples = dataset[:10]
|
|
77
|
+
for sample in dataset:
|
|
78
|
+
print(sample.file_name)
|
|
79
|
+
sample.metadata["new_key"] = "new_value"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For filtering or ordering samples first, use the query interface:
|
|
83
|
+
```python
|
|
84
|
+
from lightly_studio.core.dataset_query.sample_field import SampleField
|
|
85
|
+
|
|
86
|
+
dataset = Dataset.load("my_dataset")
|
|
87
|
+
query = dataset.match(SampleField.width > 10).order_by(SampleField.file_name)
|
|
88
|
+
for sample in query:
|
|
89
|
+
...
|
|
90
|
+
```
|
|
57
91
|
"""
|
|
58
92
|
|
|
59
93
|
def __init__(self, dataset: DatasetTable) -> None:
|
|
@@ -84,7 +118,10 @@ class Dataset:
|
|
|
84
118
|
dataset = dataset_resolver.get_by_name(session=db_manager.persistent_session(), name=name)
|
|
85
119
|
if dataset is None:
|
|
86
120
|
raise ValueError(f"Dataset with name '{name}' not found.")
|
|
87
|
-
|
|
121
|
+
# If we have embeddings in the database enable the FSC and embedding search features.
|
|
122
|
+
_enable_embedding_features_if_available(
|
|
123
|
+
session=db_manager.persistent_session(), dataset_id=dataset.dataset_id
|
|
124
|
+
)
|
|
88
125
|
return Dataset(dataset=dataset)
|
|
89
126
|
|
|
90
127
|
@staticmethod
|
|
@@ -97,6 +134,10 @@ class Dataset:
|
|
|
97
134
|
if dataset is None:
|
|
98
135
|
return Dataset.create(name=name)
|
|
99
136
|
|
|
137
|
+
# If we have embeddings in the database enable the FSC and embedding search features.
|
|
138
|
+
_enable_embedding_features_if_available(
|
|
139
|
+
session=db_manager.persistent_session(), dataset_id=dataset.dataset_id
|
|
140
|
+
)
|
|
100
141
|
return Dataset(dataset=dataset)
|
|
101
142
|
|
|
102
143
|
def __iter__(self) -> Iterator[Sample]:
|
|
@@ -500,6 +541,9 @@ def _generate_embeddings(session: Session, dataset_id: UUID, sample_ids: list[UU
|
|
|
500
541
|
# Mark the embedding search feature as enabled.
|
|
501
542
|
if "embeddingSearchEnabled" not in features.lightly_studio_active_features:
|
|
502
543
|
features.lightly_studio_active_features.append("embeddingSearchEnabled")
|
|
544
|
+
# Mark the FSC feature as enabled.
|
|
545
|
+
if "fewShotClassifierEnabled" not in features.lightly_studio_active_features:
|
|
546
|
+
features.lightly_studio_active_features.append("fewShotClassifierEnabled")
|
|
503
547
|
|
|
504
548
|
|
|
505
549
|
def _resolve_yolo_splits(data_yaml: Path, input_split: str | None) -> list[str]:
|
|
@@ -520,3 +564,46 @@ def _resolve_yolo_splits(data_yaml: Path, input_split: str | None) -> list[str]:
|
|
|
520
564
|
if not splits:
|
|
521
565
|
raise ValueError(f"No splits found in config file '{data_yaml}'")
|
|
522
566
|
return splits
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def _are_embeddings_available(session: Session, dataset_id: UUID) -> bool:
|
|
570
|
+
"""Check if there are any embeddings available for the given dataset.
|
|
571
|
+
|
|
572
|
+
Args:
|
|
573
|
+
session: Database session for resolver operations.
|
|
574
|
+
dataset_id: The ID of the dataset to check for embeddings.
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
True if embeddings exist for the dataset, False otherwise.
|
|
578
|
+
"""
|
|
579
|
+
embedding_manager = EmbeddingManagerProvider.get_embedding_manager()
|
|
580
|
+
model_id = embedding_manager.load_or_get_default_model(
|
|
581
|
+
session=session,
|
|
582
|
+
dataset_id=dataset_id,
|
|
583
|
+
)
|
|
584
|
+
if model_id is None:
|
|
585
|
+
# No default embedding model loaded for this dataset.
|
|
586
|
+
return False
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
len(
|
|
590
|
+
sample_embedding_resolver.get_all_by_dataset_id(
|
|
591
|
+
session=session, dataset_id=dataset_id, embedding_model_id=model_id
|
|
592
|
+
)
|
|
593
|
+
)
|
|
594
|
+
> 0
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def _enable_embedding_features_if_available(session: Session, dataset_id: UUID) -> None:
|
|
599
|
+
"""Enable embedding-related features if embeddings are available in the DB.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
session: Database session for resolver operations.
|
|
603
|
+
dataset_id: The ID of the dataset to check for embeddings.
|
|
604
|
+
"""
|
|
605
|
+
if _are_embeddings_available(session=session, dataset_id=dataset_id):
|
|
606
|
+
if "embeddingSearchEnabled" not in features.lightly_studio_active_features:
|
|
607
|
+
features.lightly_studio_active_features.append("embeddingSearchEnabled")
|
|
608
|
+
if "fewShotClassifierEnabled" not in features.lightly_studio_active_features:
|
|
609
|
+
features.lightly_studio_active_features.append("fewShotClassifierEnabled")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .boolean_expression import AND, NOT, OR
|
|
2
|
+
from .dataset_query import DatasetQuery
|
|
3
|
+
from .order_by import OrderByExpression, OrderByField
|
|
4
|
+
from .sample_field import SampleField
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AND",
|
|
8
|
+
"NOT",
|
|
9
|
+
"OR",
|
|
10
|
+
"DatasetQuery",
|
|
11
|
+
"OrderByExpression",
|
|
12
|
+
"OrderByField",
|
|
13
|
+
"SampleField",
|
|
14
|
+
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Dataset query utilities for filtering, ordering, and slicing samples."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -20,7 +20,103 @@ _SliceType = slice # to avoid shadowing built-in slice in type annotations
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class DatasetQuery:
|
|
23
|
-
"""Class for executing
|
|
23
|
+
"""Class for executing a query on a dataset.
|
|
24
|
+
|
|
25
|
+
# Filtering, ordering, and slicing samples in a dataset
|
|
26
|
+
Allows filtering, ordering, and slicing of samples in a dataset.
|
|
27
|
+
This class can be accessed via calling `.query()` on a Dataset instance.
|
|
28
|
+
```python
|
|
29
|
+
dataset : Dataset = ...
|
|
30
|
+
query = dataset.query()
|
|
31
|
+
```
|
|
32
|
+
The `match()`, `order_by()`, and `slice()` methods can be chained in this order.
|
|
33
|
+
You can also access the methods directly on the Dataset instance:
|
|
34
|
+
```python
|
|
35
|
+
dataset.match(...) # shorthand for dataset.query().match(...)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The object is converted to a SQL query that is lazily evaluated when iterating over
|
|
39
|
+
it or converting it to a list.
|
|
40
|
+
|
|
41
|
+
## match() - Filtering samples
|
|
42
|
+
Filtering is done via the `match()` method.
|
|
43
|
+
```python
|
|
44
|
+
from lightly_studio.core.dataset_query import SampleField
|
|
45
|
+
|
|
46
|
+
query_1 = dataset.query().match(SampleField.width > 100)
|
|
47
|
+
query_2 = dataset.query().match(SampleField.tags.contains('cat'))
|
|
48
|
+
```
|
|
49
|
+
AND and OR operators are available for combining multiple conditions.
|
|
50
|
+
```python
|
|
51
|
+
from lightly_studio.core.dataset_query import SampleField, AND, OR
|
|
52
|
+
|
|
53
|
+
query = dataset.query().match(
|
|
54
|
+
AND(
|
|
55
|
+
SampleField.height < 200,
|
|
56
|
+
OR(
|
|
57
|
+
SampleField.file_name == 'image.png',
|
|
58
|
+
SampleField.file_name == 'image2.png',
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## order_by() - Ordering samples
|
|
65
|
+
The results can be ordered by using `order_by()`. For tie-breaking, multiple fields
|
|
66
|
+
can be provided. The first field has the highest priority. The default is
|
|
67
|
+
ascending order. To order in descending order, use `OrderByField(...).desc()`.
|
|
68
|
+
```python
|
|
69
|
+
from lightly_studio.core.dataset_query import OrderByField, SampleField
|
|
70
|
+
query = query.order_by(
|
|
71
|
+
OrderByField(SampleField.width),
|
|
72
|
+
OrderByField(SampleField.file_name).desc()
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## slice() - Slicing samples
|
|
77
|
+
Slicing can be applied via `slice()` or bracket notation.
|
|
78
|
+
```python
|
|
79
|
+
query = query.slice(offset=10, limit=20)
|
|
80
|
+
query = query[10:30] # equivalent to slice(offset=10, limit=20)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
# Usage of the filtered, ordered and sliced query
|
|
84
|
+
|
|
85
|
+
## Iterating and converting to list
|
|
86
|
+
Finally, the query can be executed by iterating over it or converting to a list.
|
|
87
|
+
```python
|
|
88
|
+
for sample in query:
|
|
89
|
+
print(sample.file_name)
|
|
90
|
+
samples = query.to_list()
|
|
91
|
+
```
|
|
92
|
+
The samples returned are instances of the `Sample` class. They are writable, and
|
|
93
|
+
changes to them will be persisted to the database.
|
|
94
|
+
|
|
95
|
+
## Adding tags to matching samples
|
|
96
|
+
The filtered set can also be used to add a tag to all matching samples.
|
|
97
|
+
```python
|
|
98
|
+
query.add_tag('my_tag')
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Selecting a subset of samples using smart selection
|
|
102
|
+
A Selection interface can be created from the current query results. It will only
|
|
103
|
+
select the samples matching the current query at the time of calling selection().
|
|
104
|
+
```python
|
|
105
|
+
# Choosing 100 diverse samples from the 'cat' tag.
|
|
106
|
+
# Save them under the tag name "diverse_cats".
|
|
107
|
+
selection = dataset.query().match(
|
|
108
|
+
SampleField.tags.contains('cat')
|
|
109
|
+
).selection()
|
|
110
|
+
selection.diverse(100, "diverse_cats")
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Exporting the query results
|
|
114
|
+
An export interface can be created from the current query results.
|
|
115
|
+
```python
|
|
116
|
+
export = dataset.query().match(...).export()
|
|
117
|
+
export.to_coco_object_detections('/path/to/coco.json')
|
|
118
|
+
```
|
|
119
|
+
"""
|
|
24
120
|
|
|
25
121
|
def __init__(self, dataset: DatasetTable, session: Session) -> None:
|
|
26
122
|
"""Initialize with dataset and database session.
|
|
@@ -16,8 +16,17 @@ from lightly_studio.models.sample import SampleTable
|
|
|
16
16
|
class SampleField:
|
|
17
17
|
"""Providing access to predefined sample fields for queries.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
It is used for the `query.match(...)` and `query.order_by(...)` methods of the
|
|
20
|
+
`DatasetQuery` class.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from lightly_studio.core.dataset_query import DatasetQuery, SampleField, OrderByField
|
|
24
|
+
|
|
25
|
+
query = dataset.query()
|
|
26
|
+
query.match(SampleField.tags.contains("cat"))
|
|
27
|
+
query.order_by(OrderByField(SampleField.file_path_abs))
|
|
28
|
+
samples = query.to_list()
|
|
29
|
+
```
|
|
21
30
|
"""
|
|
22
31
|
|
|
23
32
|
file_name = StringField(col(SampleTable.file_name))
|
lightly_studio/core/sample.py
CHANGED
|
@@ -50,7 +50,39 @@ class DBField(Generic[T]):
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class Sample:
|
|
53
|
-
"""Interface to a dataset sample.
|
|
53
|
+
"""Interface to a dataset sample.
|
|
54
|
+
|
|
55
|
+
It is usually returned by a query to the dataset.
|
|
56
|
+
```python
|
|
57
|
+
for sample in dataset:
|
|
58
|
+
...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Many properties of the sample are directly accessible as attributes of this class.
|
|
62
|
+
```python
|
|
63
|
+
print(f"Sample file name: {sample.file_name}")
|
|
64
|
+
print(f"Sample file path: {sample.file_path_abs}")
|
|
65
|
+
print(f"Sample width: {sample.width}")
|
|
66
|
+
print(f"Sample height: {sample.height}")
|
|
67
|
+
```
|
|
68
|
+
Note that some attributes like the `sample_id` are technically writable, but changing
|
|
69
|
+
them is not recommended and may lead to inconsistent states.
|
|
70
|
+
|
|
71
|
+
Access sample's metadata via the `metadata` property, which
|
|
72
|
+
provides a dictionary-like interface to get and set metadata key-value pairs.
|
|
73
|
+
```python
|
|
74
|
+
some_value = sample.metadata["some_key"]
|
|
75
|
+
sample.metadata["another_key"] = "new_value"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Access sample's tags via the `tags` property.
|
|
79
|
+
```python
|
|
80
|
+
sample.tags = ["tag1", "tag2"] # Replace all tags
|
|
81
|
+
print(f"Current tags: {sample.tags}")
|
|
82
|
+
sample.add_tag("tag_3")
|
|
83
|
+
sample.remove_tag("tag_1")
|
|
84
|
+
```
|
|
85
|
+
"""
|
|
54
86
|
|
|
55
87
|
file_name = DBField(col(SampleTable.file_name))
|
|
56
88
|
width = DBField(col(SampleTable.width))
|
lightly_studio/db_manager.py
CHANGED
|
@@ -47,8 +47,10 @@ class DatabaseEngine:
|
|
|
47
47
|
@contextmanager
|
|
48
48
|
def session(self) -> Generator[Session, None, None]:
|
|
49
49
|
"""Create a short-lived database session. The session is autoclosed."""
|
|
50
|
-
#
|
|
51
|
-
#
|
|
50
|
+
# Commit the persistent session before creating a short-lived session.
|
|
51
|
+
# This prevents a foreign key constraint violation issue if the short-lived
|
|
52
|
+
# session attempts a delete of an object referencing an object modified
|
|
53
|
+
# in the persistent session.
|
|
52
54
|
if self.get_persistent_session().in_transaction():
|
|
53
55
|
logging.debug("The persistent session is in transaction, committing changes.")
|
|
54
56
|
self.get_persistent_session().commit()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const env={"PUBLIC_SAMPLES_URL":"/images","PUBLIC_LIGHTLY_STUDIO_API_URL":"/"}
|
|
1
|
+
export const env={"PUBLIC_SAMPLES_URL":"/images","PUBLIC_LIGHTLY_STUDIO_API_URL":"/","PUBLIC_POSTHOG_DEV_KEY":"phc_A9K0pMRovzmhFhngbKAZIr2qZdA14eHvsZY6kjNdYWr","PUBLIC_POSTHOG_KEY":"","PUBLIC_POSTHOG_HOST":"https://eu.i.posthog.com"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:Open Sans;src:url(./OpenSans-VariableFont_wdth_wght.BZBpG5Iz.ttf) format("truetype-variations");font-weight:100 1000;font-style:normal;font-display:swap}@font-face{font-family:Open Sans;src:url(./OpenSans-Italic-VariableFont_wdth_wght.B4AZ-wl6.ttf) format("truetype-variations");font-style:italic;font-weight:100 1000;font-display:swap}@font-face{font-family:Open Sans;src:url(./OpenSans-Regular.DxJTClRG.ttf) format("truetype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:Open Sans;src:url(./OpenSans-Medium.DVUZMR_6.ttf) format("truetype");font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:Open Sans;src:url(./OpenSans-SemiBold.D3TTYgdB.ttf) format("truetype");font-weight:600;font-style:normal;font-display:swap}@font-face{font-family:Open Sans;src:url(./OpenSans-Bold.DGvYQtcs.ttf) format("truetype");font-weight:700;font-style:normal;font-display:swap}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background: 0 0% 100%;--foreground: 240 10% 3.9%;--card: 0 0% 100%;--card-foreground: 240 10% 3.9%;--diffuse: 0 0% 100%;--diffuse-foreground: 240 10% 3.9%;--popover: 0 0% 100%;--popover-foreground: 240 10% 3.9%;--primary: 42.1 76.2% 36.3%;--primary-foreground: 355.7 100% 97.3%;--secondary: 240 4.8% 95.9%;--secondary-foreground: 240 5.9% 10%;--muted: 240 4.8% 95.9%;--muted-foreground: 240 3.8% 46.1%;--accent: 240 4.8% 95.9%;--accent-foreground: 240 5.9% 10%;--destructive: 0 72.22% 50.59%;--destructive-foreground: 0 0% 98%;--border: 240 5.9% 90%;--border-hard: 240 5.9% 90%;--input: 240 5.9% 90%;--ring: 142.1 76.2% 36.3%;--radius: .5rem}.dark{--background: 20 14.3% 4.1%;--foreground: 0 0% 95%;--card: 220 16% 11%;--card-foreground: 0 0% 95%;--diffuse: 0 0% 95%;--diffuse-foreground: 0 0% 73.73%;--popover: 0 0% 9%;--popover-foreground: 0 0% 95%;--primary: var(--color-lightly-primary);--primary-foreground: 144.9 80.4% 10%;--secondary: 240 3.7% 15.9%;--secondary-foreground: 0 0% 98%;--muted: 0 0% 15%;--muted-foreground: 240 5% 64.9%;--accent: 12 6.5% 15.1%;--accent-foreground: 0 0% 98%;--destructive: 0 62.8% 30.6%;--destructive-foreground: 0 85.7% 97.3%;--border: 240 3.7% 15.9%;--border-hard: 223 12% 66%;--input: 240 3.7% 15.9%;--ring: 142.4 71.8% 29.2%}*{--tw-border-opacity: 1;border-color:hsl(var(--border) / var(--tw-border-opacity, 1))}body{--tw-bg-opacity: 1;background-color:hsl(var(--background) / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1));font-family:Open Sans,sans-serif;font-size:16px}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width: 1400px){.container{max-width:1400px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.bottom-0{bottom:0}.left-0{left:0}.left-1{left:.25rem}.left-2{left:.5rem}.left-4{left:1rem}.left-\[50\%\]{left:50%}.right-0{right:0}.right-4{right:1rem}.right-7{right:1.75rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-4{top:1rem}.top-\[50\%\]{top:50%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-50{z-index:50}.col-span-3{grid-column:span 3 / span 3}.m-0{margin:0}.m-2{margin:.5rem}.-mx-1{margin-left:-.25rem;margin-right:-.25rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.ml-1{margin-left:.25rem}.ml-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.box-content{box-sizing:content-box}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.size-12{width:3rem;height:3rem}.size-3\.5{width:.875rem;height:.875rem}.size-4{width:1rem;height:1rem}.size-5{width:1.25rem;height:1.25rem}.size-6{width:1.5rem;height:1.5rem}.size-8{width:2rem;height:2rem}.size-9{width:2.25rem;height:2.25rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-2{height:.5rem}.h-28{height:7rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[1px\]{height:1px}.h-\[90vh\]{height:90vh}.h-\[var\(--bits-select-anchor-height\)\]{height:var(--bits-select-anchor-height)}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-48{max-height:12rem}.max-h-96{max-height:24rem}.max-h-\[300px\]{max-height:300px}.min-h-0{min-height:0px}.min-h-44{min-height:11rem}.min-h-full{min-height:100%}.w-1{width:.25rem}.w-10{width:2.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-4\/12{width:33.333333%}.w-56{width:14rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-9{width:2.25rem}.w-\[1px\]{width:1px}.w-\[200px\]{width:200px}.w-\[320px\]{width:320px}.w-\[32px\]{width:32px}.w-\[375px\]{width:375px}.w-\[400px\]{width:400px}.w-\[480px\]{width:480px}.w-auto{width:auto}.w-full{width:100%}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:0px}.min-w-\[250px\]{min-width:250px}.min-w-\[8rem\]{min-width:8rem}.min-w-\[var\(--bits-select-anchor-width\)\]{min-width:var(--bits-select-anchor-width)}.max-w-\[150px\]{max-width:150px}.max-w-\[200px\]{max-width:200px}.max-w-\[30\%\]{max-width:30%}.max-w-lg{max-width:32rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-\[-50\%\]{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-\[-50\%\]{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate: 180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-col-resize{cursor:col-resize}.cursor-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize{resize:both}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.grid-cols-\[6rem_1fr\]{grid-template-columns:6rem 1fr}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0{gap:0px}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-10{gap:2.5rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-y-3{row-gap:.75rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.25rem * var(--tw-space-x-reverse));margin-left:calc(.25rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(1rem * var(--tw-space-x-reverse));margin-left:calc(1rem * calc(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-nowrap{text-wrap:nowrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-\[1vw\]{border-radius:1vw}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-md{border-radius:calc(var(--radius) - 2px)}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-b-lg{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.border{border-width:1px}.border-0{border-width:0px}.border-2{border-width:2px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent-foreground\/20{border-color:hsl(var(--accent-foreground) / .2)}.border-border{--tw-border-opacity: 1;border-color:hsl(var(--border) / var(--tw-border-opacity, 1))}.border-border-hard{border-color:hsl(var(--border-hard) / .2)}.border-destructive\/50{border-color:hsl(var(--destructive) / .5)}.border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.border-input{--tw-border-opacity: 1;border-color:hsl(var(--input) / var(--tw-border-opacity, 1))}.border-primary{--tw-border-opacity: 1;border-color:hsl(var(--primary) / var(--tw-border-opacity, 1))}.border-red-400{--tw-border-opacity: 1;border-color:rgb(248 113 113 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity: 1;border-color:rgb(255 255 255 / var(--tw-border-opacity, 1))}.bg-accent{--tw-bg-opacity: 1;background-color:hsl(var(--accent) / var(--tw-bg-opacity, 1))}.bg-background{--tw-bg-opacity: 1;background-color:hsl(var(--background) / var(--tw-bg-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-black\/30{background-color:#0000004d}.bg-black\/60{background-color:#0009}.bg-black\/80{background-color:#000c}.bg-border{--tw-bg-opacity: 1;background-color:hsl(var(--border) / var(--tw-bg-opacity, 1))}.bg-border-hard{background-color:hsl(var(--border-hard) / .2)}.bg-card{--tw-bg-opacity: 1;background-color:hsl(var(--card) / var(--tw-bg-opacity, 1))}.bg-destructive{--tw-bg-opacity: 1;background-color:hsl(var(--destructive) / var(--tw-bg-opacity, 1))}.bg-muted{--tw-bg-opacity: 1;background-color:hsl(var(--muted) / var(--tw-bg-opacity, 1))}.bg-muted\/30{background-color:hsl(var(--muted) / .3)}.bg-muted\/50{background-color:hsl(var(--muted) / .5)}.bg-popover{--tw-bg-opacity: 1;background-color:hsl(var(--popover) / var(--tw-bg-opacity, 1))}.bg-primary{--tw-bg-opacity: 1;background-color:hsl(var(--primary) / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-secondary{--tw-bg-opacity: 1;background-color:hsl(var(--secondary) / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-opacity-20{--tw-bg-opacity: .2}.bg-opacity-80{--tw-bg-opacity: .8}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-0{padding-bottom:0}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-start{text-align:start}.text-end{text-align:end}.align-baseline{vertical-align:baseline}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.025em}.tracking-widest{letter-spacing:.1em}.text-current{color:currentColor}.text-destructive{--tw-text-opacity: 1;color:hsl(var(--destructive) / var(--tw-text-opacity, 1))}.text-destructive-foreground{--tw-text-opacity: 1;color:hsl(var(--destructive-foreground) / var(--tw-text-opacity, 1))}.text-diffuse-foreground{--tw-text-opacity: 1;color:hsl(var(--diffuse-foreground) / var(--tw-text-opacity, 1))}.text-foreground{--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1))}.text-foreground\/80{color:hsl(var(--foreground) / .8)}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-green-600{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-muted-foreground{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.text-orange-600{--tw-text-opacity: 1;color:rgb(234 88 12 / var(--tw-text-opacity, 1))}.text-popover-foreground{--tw-text-opacity: 1;color:hsl(var(--popover-foreground) / var(--tw-text-opacity, 1))}.text-primary{--tw-text-opacity: 1;color:hsl(var(--primary) / var(--tw-text-opacity, 1))}.text-primary-foreground{--tw-text-opacity: 1;color:hsl(var(--primary-foreground) / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity: 1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-secondary-foreground{--tw-text-opacity: 1;color:hsl(var(--secondary-foreground) / var(--tw-text-opacity, 1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring-0{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-2{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-primary{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--primary) / var(--tw-ring-opacity, 1))}.ring-ring{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--ring) / var(--tw-ring-opacity, 1))}.ring-offset-background{--tw-ring-offset-color: hsl(var(--background) / 1)}.drop-shadow{--tw-drop-shadow: drop-shadow(0 1px 2px rgb(0 0 0 / .1)) drop-shadow(0 1px 1px rgb(0 0 0 / .06));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert: invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur: blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.animate-in{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.fade-in-0{--tw-enter-opacity: 0}.zoom-in{--tw-enter-scale: 0}.zoom-in-95{--tw-enter-scale: .95}.zoom-out{--tw-exit-scale: 0}.duration-200{animation-duration:.2s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}@custom-variant dark (&:where(.dark,.dark *));:root{--color-lightly-primary: 159 64% 54%}.file\:border-0::file-selector-button{border-width:0px}.file\:bg-transparent::file-selector-button{background-color:transparent}.file\:text-sm::file-selector-button{font-size:.875rem;line-height:1.25rem}.file\:font-medium::file-selector-button{font-weight:500}.placeholder\:text-muted-foreground::-moz-placeholder{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.placeholder\:text-muted-foreground::placeholder{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.hover\:bg-accent:hover{--tw-bg-opacity: 1;background-color:hsl(var(--accent) / var(--tw-bg-opacity, 1))}.hover\:bg-accent\/50:hover{background-color:hsl(var(--accent) / .5)}.hover\:bg-destructive\/90:hover{background-color:hsl(var(--destructive) / .9)}.hover\:bg-muted\/50:hover{background-color:hsl(var(--muted) / .5)}.hover\:bg-primary\/90:hover{background-color:hsl(var(--primary) / .9)}.hover\:bg-secondary\/80:hover{background-color:hsl(var(--secondary) / .8)}.hover\:text-accent-foreground:hover{--tw-text-opacity: 1;color:hsl(var(--accent-foreground) / var(--tw-text-opacity, 1))}.hover\:text-destructive:hover{--tw-text-opacity: 1;color:hsl(var(--destructive) / var(--tw-text-opacity, 1))}.hover\:text-foreground:hover{--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-ring:focus{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--ring) / var(--tw-ring-opacity, 1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--ring) / var(--tw-ring-opacity, 1))}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: hsl(var(--background) / 1)}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.peer:disabled~.peer-disabled\:cursor-not-allowed{cursor:not-allowed}.peer:disabled~.peer-disabled\:opacity-70{opacity:.7}.aria-selected\:bg-accent[aria-selected=true]{--tw-bg-opacity: 1;background-color:hsl(var(--accent) / var(--tw-bg-opacity, 1))}.aria-selected\:text-accent-foreground[aria-selected=true]{--tw-text-opacity: 1;color:hsl(var(--accent-foreground) / var(--tw-text-opacity, 1))}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[orientation\=\'horizontal\'\]\:h-2[data-orientation=horizontal]{height:.5rem}.data-\[orientation\=\'horizontal\'\]\:h-full[data-orientation=horizontal],.data-\[orientation\=\'vertical\'\]\:h-full[data-orientation=vertical]{height:100%}.data-\[orientation\=\'vertical\'\]\:min-h-44[data-orientation=vertical]{min-height:11rem}.data-\[orientation\=\'horizontal\'\]\:w-full[data-orientation=horizontal]{width:100%}.data-\[orientation\=\'vertical\'\]\:w-2[data-orientation=vertical]{width:.5rem}.data-\[orientation\=\'vertical\'\]\:w-auto[data-orientation=vertical]{width:auto}.data-\[orientation\=\'vertical\'\]\:w-full[data-orientation=vertical]{width:100%}.data-\[side\=bottom\]\:translate-y-1[data-side=bottom]{--tw-translate-y: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=left\]\:-translate-x-1[data-side=left]{--tw-translate-x: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=right\]\:translate-x-1[data-side=right]{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[side\=top\]\:-translate-y-1[data-side=top]{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=checked\]\:translate-x-4[data-state=checked]{--tw-translate-x: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=unchecked\]\:translate-x-0[data-state=unchecked]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[disabled\=true\]\:cursor-not-allowed[data-disabled=true]{cursor:not-allowed}.data-\[orientation\=\'vertical\'\]\:flex-col[data-orientation=vertical]{flex-direction:column}.data-\[highlighted\]\:bg-accent[data-highlighted]{--tw-bg-opacity: 1;background-color:hsl(var(--accent) / var(--tw-bg-opacity, 1))}.data-\[state\=active\]\:bg-background[data-state=active]{--tw-bg-opacity: 1;background-color:hsl(var(--background) / var(--tw-bg-opacity, 1))}.data-\[state\=checked\]\:bg-primary[data-state=checked]{--tw-bg-opacity: 1;background-color:hsl(var(--primary) / var(--tw-bg-opacity, 1))}.data-\[state\=open\]\:bg-secondary[data-state=open]{--tw-bg-opacity: 1;background-color:hsl(var(--secondary) / var(--tw-bg-opacity, 1))}.data-\[state\=unchecked\]\:bg-input[data-state=unchecked]{--tw-bg-opacity: 1;background-color:hsl(var(--input) / var(--tw-bg-opacity, 1))}.data-\[highlighted\]\:text-accent-foreground[data-highlighted]{--tw-text-opacity: 1;color:hsl(var(--accent-foreground) / var(--tw-text-opacity, 1))}.data-\[placeholder\]\:text-muted-foreground[data-placeholder]{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / var(--tw-text-opacity, 1))}.data-\[state\=active\]\:text-foreground[data-state=active]{--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1))}.data-\[state\=checked\]\:text-primary-foreground[data-state=checked]{--tw-text-opacity: 1;color:hsl(var(--primary-foreground) / var(--tw-text-opacity, 1))}.data-\[disabled\=true\]\:opacity-50[data-disabled=true],.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[state\=active\]\:shadow-sm[data-state=active]{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.data-\[state\=closed\]\:duration-300[data-state=closed]{transition-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{transition-duration:.5s}.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[side\=bottom\]\:slide-in-from-top-2[data-side=bottom]{--tw-enter-translate-y: -.5rem}.data-\[side\=left\]\:slide-in-from-right-2[data-side=left]{--tw-enter-translate-x: .5rem}.data-\[side\=right\]\:slide-in-from-left-2[data-side=right]{--tw-enter-translate-x: -.5rem}.data-\[side\=top\]\:slide-in-from-bottom-2[data-side=top]{--tw-enter-translate-y: .5rem}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100%}.data-\[state\=closed\]\:slide-out-to-left-1\/2[data-state=closed]{--tw-exit-translate-x: -50%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed]{--tw-exit-translate-x: 100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100%}.data-\[state\=closed\]\:slide-out-to-top-\[48\%\][data-state=closed]{--tw-exit-translate-y: -48%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100%}.data-\[state\=open\]\:slide-in-from-left-1\/2[data-state=open]{--tw-enter-translate-x: -50%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=open\]\:slide-in-from-top-\[48\%\][data-state=open]{--tw-enter-translate-y: -48%}.data-\[state\=closed\]\:duration-300[data-state=closed]{animation-duration:.3s}.data-\[state\=open\]\:duration-500[data-state=open]{animation-duration:.5s}.dark\:border-destructive:is(.dark *){--tw-border-opacity: 1;border-color:hsl(var(--destructive) / var(--tw-border-opacity, 1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.dark\:text-gray-600:is(.dark *){--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.dark\:\[color-scheme\:dark\]:is(.dark *){color-scheme:dark}@media (min-width: 640px){.sm\:inline{display:inline}.sm\:max-h-\[90vh\]{max-height:90vh}.sm\:max-w-\[425px\]{max-width:425px}.sm\:max-w-\[500px\]{max-width:500px}.sm\:max-w-\[800px\]{max-width:800px}.sm\:max-w-sm{max-width:24rem}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:gap-2\.5{gap:.625rem}.sm\:space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.sm\:rounded-lg{border-radius:var(--radius)}.sm\:text-left{text-align:left}}@media (min-width: 768px){.md\:text-sm{font-size:.875rem;line-height:1.25rem}}.\[\&\>span\]\:line-clamp-1>span{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.\[\&\>svg\]\:absolute>svg{position:absolute}.\[\&\>svg\]\:left-4>svg{left:1rem}.\[\&\>svg\]\:top-4>svg{top:1rem}.\[\&\>svg\]\:size-3\.5>svg{width:.875rem;height:.875rem}.\[\&\>svg\]\:text-destructive>svg{--tw-text-opacity: 1;color:hsl(var(--destructive) / var(--tw-text-opacity, 1))}.\[\&\>svg\]\:text-foreground>svg{--tw-text-opacity: 1;color:hsl(var(--foreground) / var(--tw-text-opacity, 1))}.\[\&\>svg\~\*\]\:pl-7>svg~*{padding-left:1.75rem}.\[\&_\[data-command-group\]\:not\(\[hidden\]\)_\~\[data-command-group\]\]\:pt-0 [data-command-group]:not([hidden])~[data-command-group]{padding-top:0}.\[\&_\[data-command-group\]\]\:px-2 [data-command-group]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[data-command-input-wrapper\]_svg\]\:h-5 [data-command-input-wrapper] svg{height:1.25rem}.\[\&_\[data-command-input-wrapper\]_svg\]\:w-5 [data-command-input-wrapper] svg{width:1.25rem}.\[\&_\[data-command-input\]\]\:h-12 [data-command-input]{height:3rem}.\[\&_\[data-command-item\]\]\:px-2 [data-command-item]{padding-left:.5rem;padding-right:.5rem}.\[\&_\[data-command-item\]\]\:py-3 [data-command-item]{padding-top:.75rem;padding-bottom:.75rem}.\[\&_\[data-command-item\]_svg\]\:h-5 [data-command-item] svg{height:1.25rem}.\[\&_\[data-command-item\]_svg\]\:w-5 [data-command-item] svg{width:1.25rem}.\[\&_p\]\:leading-relaxed p{line-height:1.625}.\[\&_svg\]\:pointer-events-none svg{pointer-events:none}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}html[dir=ltr],[data-sonner-toaster][dir=ltr]{--toast-icon-margin-start: -3px;--toast-icon-margin-end: 4px;--toast-svg-margin-start: -1px;--toast-svg-margin-end: 0px;--toast-button-margin-start: auto;--toast-button-margin-end: 0;--toast-close-button-start: 0;--toast-close-button-end: unset;--toast-close-button-transform: translate(-35%, -35%)}html[dir=rtl],[data-sonner-toaster][dir=rtl]{--toast-icon-margin-start: 4px;--toast-icon-margin-end: -3px;--toast-svg-margin-start: 0px;--toast-svg-margin-end: -1px;--toast-button-margin-start: 0;--toast-button-margin-end: auto;--toast-close-button-start: unset;--toast-close-button-end: 0;--toast-close-button-transform: translate(35%, -35%)}[data-sonner-toaster]{position:fixed;width:var(--width);font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;--gray1: hsl(0, 0%, 99%);--gray2: hsl(0, 0%, 97.3%);--gray3: hsl(0, 0%, 95.1%);--gray4: hsl(0, 0%, 93%);--gray5: hsl(0, 0%, 90.9%);--gray6: hsl(0, 0%, 88.7%);--gray7: hsl(0, 0%, 85.8%);--gray8: hsl(0, 0%, 78%);--gray9: hsl(0, 0%, 56.1%);--gray10: hsl(0, 0%, 52.3%);--gray11: hsl(0, 0%, 43.5%);--gray12: hsl(0, 0%, 9%);--border-radius: 8px;box-sizing:border-box;padding:0;margin:0;list-style:none;outline:none;z-index:999999999;transition:transform .4s ease}@media (hover: none) and (pointer: coarse){[data-sonner-toaster][data-lifted=true]{transform:none}}[data-sonner-toaster][data-x-position=right]{right:var(--offset-right)}[data-sonner-toaster][data-x-position=left]{left:var(--offset-left)}[data-sonner-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-sonner-toaster][data-y-position=top]{top:var(--offset-top)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--offset-bottom)}[data-sonner-toast]{--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));z-index:var(--z-index);position:absolute;opacity:0;transform:var(--y);touch-action:none;transition:transform .4s,opacity .4s,height .4s,box-shadow .2s;box-sizing:border-box;outline:none;overflow-wrap:anywhere}[data-sonner-toast][data-styled=true]{padding:16px;background:var(--normal-bg);border:1px solid var(--normal-border);color:var(--normal-text);border-radius:var(--border-radius);box-shadow:0 4px 12px #0000001a;width:var(--width);font-size:13px;display:flex;align-items:center;gap:6px}[data-sonner-toast]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-y-position=top]{top:0;--y: translateY(-100%);--lift: 1;--lift-amount: calc(1 * var(--gap))}[data-sonner-toast][data-y-position=bottom]{bottom:0;--y: translateY(100%);--lift: -1;--lift-amount: calc(var(--lift) * var(--gap))}[data-sonner-toast][data-styled=true] [data-description]{font-weight:400;line-height:1.4;color:#3f3f3f}[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description]{color:inherit}[data-sonner-toaster][data-sonner-theme=dark] [data-description]{color:#e8e8e8}[data-sonner-toast][data-styled=true] [data-title]{font-weight:500;line-height:1.5;color:inherit}[data-sonner-toast][data-styled=true] [data-icon]{display:flex;height:16px;width:16px;position:relative;justify-content:flex-start;align-items:center;flex-shrink:0;margin-left:var(--toast-icon-margin-start);margin-right:var(--toast-icon-margin-end)}[data-sonner-toast][data-promise=true] [data-icon]>svg{opacity:0;transform:scale(.8);transform-origin:center;animation:sonner-fade-in .3s ease forwards}[data-sonner-toast][data-styled=true] [data-icon]>*{flex-shrink:0}[data-sonner-toast][data-styled=true] [data-icon] svg{margin-left:var(--toast-svg-margin-start);margin-right:var(--toast-svg-margin-end)}[data-sonner-toast][data-styled=true] [data-content]{display:flex;flex-direction:column;gap:2px}[data-sonner-toast][data-styled=true] [data-button]{border-radius:4px;padding-left:8px;padding-right:8px;height:24px;font-size:12px;color:var(--normal-bg);background:var(--normal-text);margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end);border:none;font-weight:500;cursor:pointer;outline:none;display:flex;align-items:center;flex-shrink:0;transition:opacity .4s,box-shadow .2s}[data-sonner-toast][data-styled=true] [data-button]:focus-visible{box-shadow:0 0 0 2px #0006}[data-sonner-toast][data-styled=true] [data-button]:first-of-type{margin-left:var(--toast-button-margin-start);margin-right:var(--toast-button-margin-end)}[data-sonner-toast][data-styled=true] [data-cancel]{color:var(--normal-text);background:#00000014}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel]{background:#ffffff4d}[data-sonner-toast][data-styled=true] [data-close-button]{position:absolute;left:var(--toast-close-button-start);right:var(--toast-close-button-end);top:0;height:20px;width:20px;display:flex;justify-content:center;align-items:center;padding:0;color:var(--gray12);background:var(--normal-bg);border:1px solid var(--gray4);transform:var(--toast-close-button-transform);border-radius:50%;cursor:pointer;z-index:1;transition:opacity .1s,background .2s,border-color .2s}[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible{box-shadow:0 4px 12px #0000001a,0 0 0 2px #0003}[data-sonner-toast][data-styled=true] [data-disabled=true]{cursor:not-allowed}[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover{background:var(--gray2);border-color:var(--gray5)}[data-sonner-toast][data-swiping=true]:before{content:"";position:absolute;left:-100%;right:-100%;height:100%;z-index:-1}[data-sonner-toast][data-y-position=top][data-swiping=true]:before{bottom:50%;transform:scaleY(3) translateY(50%)}[data-sonner-toast][data-y-position=bottom][data-swiping=true]:before{top:50%;transform:scaleY(3) translateY(-50%)}[data-sonner-toast][data-swiping=false][data-removed=true]:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;transform:scaleY(2)}[data-sonner-toast][data-expanded=true]:after{content:"";position:absolute;left:0;height:calc(var(--gap) + 1px);bottom:100%;width:100%}[data-sonner-toast][data-mounted=true]{--y: translateY(0);opacity:1}[data-sonner-toast][data-expanded=false][data-front=false]{--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}[data-sonner-toast]>*{transition:opacity .4s}[data-sonner-toast][data-x-position=right]{right:0}[data-sonner-toast][data-x-position=left]{left:0}[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]>*{opacity:0}[data-sonner-toast][data-visible=false]{opacity:0;pointer-events:none}[data-sonner-toast][data-mounted=true][data-expanded=true]{--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]{--y: translateY(calc(var(--lift) * -100%));opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]{--y: translateY( calc(var(--lift) * var(--offset) + var(--lift) * -100%) );opacity:0}[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]{--y: translateY(40%);opacity:0;transition:transform .5s,opacity .2s}[data-sonner-toast][data-removed=true][data-front=false]:before{height:calc(var(--initial-height) + 20%)}[data-sonner-toast][data-swiping=true]{transform:var(--y) translateY(var(--swipe-amount-y, 0px)) translate(var(--swipe-amount-x, 0px));transition:none}[data-sonner-toast][data-swiped=true]{-webkit-user-select:none;-moz-user-select:none;user-select:none}[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],[data-sonner-toast][data-swipe-out=true][data-y-position=top]{animation-duration:.2s;animation-timing-function:ease-out;animation-fill-mode:forwards}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left]{animation-name:swipe-out-left}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right]{animation-name:swipe-out-right}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up]{animation-name:swipe-out-up}[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down]{animation-name:swipe-out-down}@keyframes swipe-out-left{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) - 100%));opacity:0}}@keyframes swipe-out-right{0%{transform:var(--y) translate(var(--swipe-amount-x));opacity:1}to{transform:var(--y) translate(calc(var(--swipe-amount-x) + 100%));opacity:0}}@keyframes swipe-out-up{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) - 100%));opacity:0}}@keyframes swipe-out-down{0%{transform:var(--y) translateY(var(--swipe-amount-y));opacity:1}to{transform:var(--y) translateY(calc(var(--swipe-amount-y) + 100%));opacity:0}}@media (max-width: 600px){[data-sonner-toaster]{position:fixed;right:var(--mobile-offset-right);left:var(--mobile-offset-left);width:100%}[data-sonner-toaster][dir=rtl]{left:calc(var(--mobile-offset-left) * -1)}[data-sonner-toaster] [data-sonner-toast]{left:0;right:0;width:calc(100% - var(--mobile-offset-left) * 2)}[data-sonner-toaster][data-x-position=left]{left:var(--mobile-offset-left)}[data-sonner-toaster][data-y-position=bottom]{bottom:var(--mobile-offset-bottom)}[data-sonner-toaster][data-y-position=top]{top:var(--mobile-offset-top)}[data-sonner-toaster][data-x-position=center]{left:var(--mobile-offset-left);right:var(--mobile-offset-right);transform:none}}[data-sonner-toaster][data-sonner-theme=light]{--normal-bg: #fff;--normal-border: var(--gray4);--normal-text: var(--gray12);--success-bg: hsl(143, 85%, 96%);--success-border: hsl(145, 92%, 87%);--success-text: hsl(140, 100%, 27%);--info-bg: hsl(208, 100%, 97%);--info-border: hsl(221, 91%, 93%);--info-text: hsl(210, 92%, 45%);--warning-bg: hsl(49, 100%, 97%);--warning-border: hsl(49, 91%, 84%);--warning-text: hsl(31, 92%, 45%);--error-bg: hsl(359, 100%, 97%);--error-border: hsl(359, 100%, 94%);--error-text: hsl(360, 100%, 45%)}[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true]{--normal-bg: #000;--normal-border: hsl(0, 0%, 20%);--normal-text: var(--gray1)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true]{--normal-bg: #fff;--normal-border: var(--gray3);--normal-text: var(--gray12)}[data-sonner-toaster][data-sonner-theme=dark]{--normal-bg: #000;--normal-bg-hover: hsl(0, 0%, 12%);--normal-border: hsl(0, 0%, 20%);--normal-border-hover: hsl(0, 0%, 25%);--normal-text: var(--gray1);--success-bg: hsl(150, 100%, 6%);--success-border: hsl(147, 100%, 12%);--success-text: hsl(150, 86%, 65%);--info-bg: hsl(215, 100%, 6%);--info-border: hsl(223, 43%, 17%);--info-text: hsl(216, 87%, 65%);--warning-bg: hsl(64, 100%, 6%);--warning-border: hsl(60, 100%, 9%);--warning-text: hsl(46, 87%, 65%);--error-bg: hsl(358, 76%, 10%);--error-border: hsl(357, 89%, 16%);--error-text: hsl(358, 100%, 81%)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]{background:var(--normal-bg);border-color:var(--normal-border);color:var(--normal-text)}[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover{background:var(--normal-bg-hover);border-color:var(--normal-border-hover)}[data-rich-colors=true][data-sonner-toast][data-type=success],[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button]{background:var(--success-bg);border-color:var(--success-border);color:var(--success-text)}[data-rich-colors=true][data-sonner-toast][data-type=info],[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button]{background:var(--info-bg);border-color:var(--info-border);color:var(--info-text)}[data-rich-colors=true][data-sonner-toast][data-type=warning],[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button]{background:var(--warning-bg);border-color:var(--warning-border);color:var(--warning-text)}[data-rich-colors=true][data-sonner-toast][data-type=error],[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button]{background:var(--error-bg);border-color:var(--error-border);color:var(--error-text)}.sonner-loading-wrapper{--size: 16px;height:var(--size);width:var(--size);position:absolute;top:0;right:0;bottom:0;left:0;z-index:10}.sonner-loading-wrapper[data-visible=false]{transform-origin:center;animation:sonner-fade-out .2s ease forwards}.sonner-spinner{position:relative;top:50%;left:50%;height:var(--size);width:var(--size)}.sonner-loading-bar{animation:sonner-spin 1.2s linear infinite;background:var(--gray11);border-radius:6px;height:8%;left:-10%;position:absolute;top:-3.9%;width:24%}.sonner-loading-bar:nth-child(1){animation-delay:-1.2s;transform:rotate(.0001deg) translate(146%)}.sonner-loading-bar:nth-child(2){animation-delay:-1.1s;transform:rotate(30deg) translate(146%)}.sonner-loading-bar:nth-child(3){animation-delay:-1s;transform:rotate(60deg) translate(146%)}.sonner-loading-bar:nth-child(4){animation-delay:-.9s;transform:rotate(90deg) translate(146%)}.sonner-loading-bar:nth-child(5){animation-delay:-.8s;transform:rotate(120deg) translate(146%)}.sonner-loading-bar:nth-child(6){animation-delay:-.7s;transform:rotate(150deg) translate(146%)}.sonner-loading-bar:nth-child(7){animation-delay:-.6s;transform:rotate(180deg) translate(146%)}.sonner-loading-bar:nth-child(8){animation-delay:-.5s;transform:rotate(210deg) translate(146%)}.sonner-loading-bar:nth-child(9){animation-delay:-.4s;transform:rotate(240deg) translate(146%)}.sonner-loading-bar:nth-child(10){animation-delay:-.3s;transform:rotate(270deg) translate(146%)}.sonner-loading-bar:nth-child(11){animation-delay:-.2s;transform:rotate(300deg) translate(146%)}.sonner-loading-bar:nth-child(12){animation-delay:-.1s;transform:rotate(330deg) translate(146%)}@keyframes sonner-fade-in{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes sonner-fade-out{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}@keyframes sonner-spin{0%{opacity:1}to{opacity:.15}}@media (prefers-reduced-motion){[data-sonner-toast],[data-sonner-toast]>*,.sonner-loading-bar{transition:none!important;animation:none!important}}.sonner-loader{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);transform-origin:center;transition:opacity .2s,transform .2s}.sonner-loader[data-visible=false]{opacity:0;transform:scale(.8) translate(-50%,-50%)}
|