lightly-studio 0.3.2__py3-none-any.whl → 0.3.3__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 -1
- lightly_studio/api/app.py +6 -4
- lightly_studio/api/db_tables.py +0 -3
- lightly_studio/api/routes/api/annotation.py +26 -0
- lightly_studio/api/routes/api/annotations/__init__.py +7 -0
- lightly_studio/api/routes/api/annotations/create_annotation.py +52 -0
- lightly_studio/api/routes/api/dataset.py +3 -5
- lightly_studio/api/routes/api/embeddings2d.py +104 -0
- lightly_studio/api/routes/api/export.py +73 -0
- lightly_studio/api/routes/api/selection.py +87 -0
- lightly_studio/core/add_samples.py +0 -9
- lightly_studio/core/dataset.py +32 -48
- lightly_studio/core/dataset_query/dataset_query.py +5 -0
- lightly_studio/dataset/env.py +4 -0
- lightly_studio/dataset/file_utils.py +13 -2
- lightly_studio/dataset/loader.py +0 -54
- lightly_studio/dataset/mobileclip_embedding_generator.py +3 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.CA_CXIBb.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.DS78jgNY.css +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/{SelectableSvgGroup.BNTuXSAe.css → index.BVs_sZj9.css} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/{SelectableSvgGroup.BBm0IWdq.css → transform.D487hwJk.css} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/6t3IJ0vQ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{gLNdjSzu.js → 8NsknIT2.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Cur71c3O.js → BND_-4Kp.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DRZO-E-T.js → BdfTHw61.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{BqBqV92V.js → BfHVnyNT.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BjkP1AHA.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BuuNVL9G.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{7YNGEs1C.js → BzKGpnl4.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{C0JiMuYn.js → CCx7Ho51.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DcGCxgpH.js → CH6P3X75.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CR2upx_Q.js +4 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CWPZrTTJ.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Ccq4ZD0B.js → Cs1XmhiF.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{OH7-C_mc.js → CwPowJfP.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CxFKfZ9T.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cxevwdid.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{C98Hk3r5.js → D4whDBUi.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/D6r9vr07.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{DqUGznj_.js → DA6bFLPR.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{2O287xak.js → DEgUu98i.js} +2 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{KpAtIldw.js → DGTPl6Gk.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Cs31G8Qn.js → DKGxBSlK.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{D8GZDMNN.js → DQXoLcsF.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DQe_kdRt.js +92 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DcY4jgG3.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Crk-jcvV.js → RmD8FzRo.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/V-MnMC1X.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/{Df3aMO5B.js → keKYsoph.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/{app.BI-EA5gL.js → app.BVr6DYqP.js} +2 -2
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.u7zsVvqp.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.Da2agmdd.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{1._I9GR805.js → 1.B11tVRJV.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{10.J2RBFrSr.js → 10.l30Zud4h.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{12.Cmqj25a-.js → 12.CgKPGcAP.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.C8HLK8mj.js +857 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{3.w9g4AcAx.js → 3.CLvg3QcJ.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{4.BBI8KwnD.js → 4.BQhDtXUI.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.-6XqWX5G.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{6.CrbkRPam.js → 6.uBV1Lhat.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{7.FomEdhD6.js → 7.BXsgoQZh.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.BkbcnUs8.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/{9.CajIG5ce.js → 9.Bkrv-Vww.js} +1 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/clustering.worker-DKqeLtG0.js +2 -0
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/workers/search.worker-vNSty3B0.js +1 -0
- lightly_studio/dist_lightly_studio_view_app/_app/version.json +1 -1
- lightly_studio/dist_lightly_studio_view_app/index.html +14 -14
- lightly_studio/export/export_dataset.py +65 -0
- lightly_studio/export/lightly_studio_label_input.py +120 -0
- lightly_studio/few_shot_classifier/classifier_manager.py +5 -26
- lightly_studio/metadata/compute_typicality.py +67 -0
- lightly_studio/models/annotation/annotation_base.py +11 -12
- lightly_studio/resolvers/annotation_label_resolver/__init__.py +2 -1
- lightly_studio/resolvers/annotation_label_resolver/get_all.py +15 -0
- lightly_studio/resolvers/annotation_resolver/__init__.py +2 -3
- lightly_studio/resolvers/annotation_resolver/create_many.py +3 -3
- lightly_studio/resolvers/annotation_resolver/delete_annotation.py +1 -1
- lightly_studio/resolvers/annotation_resolver/delete_annotations.py +7 -3
- lightly_studio/resolvers/annotation_resolver/get_by_id.py +19 -1
- lightly_studio/resolvers/annotation_resolver/update_annotation_label.py +0 -1
- lightly_studio/resolvers/annotations/annotations_filter.py +1 -11
- lightly_studio/selection/mundig.py +7 -10
- lightly_studio/selection/selection_config.py +4 -1
- lightly_studio/services/annotations_service/__init__.py +8 -0
- lightly_studio/services/annotations_service/create_annotation.py +63 -0
- lightly_studio/services/annotations_service/delete_annotation.py +22 -0
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/METADATA +152 -27
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/RECORD +89 -85
- lightly_studio/api/routes/api/annotation_task.py +0 -37
- lightly_studio/api/routes/api/metrics.py +0 -76
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/0.DenzbfeK.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/assets/_layout.T-zjSUd3.css +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BBoGk9hq.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/BRnH9v23.js +0 -92
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Bg1Y5eUZ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CG0dMCJi.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/Cpy-nab_.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CsKrY2zA.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/CzgC3GFB.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DFRh-Spp.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/DkR_EZ_B.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/M1Q1F7bw.js +0 -4
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/chunks/i0ZZ4z06.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/entry/start.CcsRl3cZ.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/0.BbO4Zc3r.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/2.C45iKJHA.js +0 -6
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/5.huHuxdiF.js +0 -1
- lightly_studio/dist_lightly_studio_view_app/_app/immutable/nodes/8.Cb_ADSLk.js +0 -1
- lightly_studio/metrics/__init__.py +0 -0
- lightly_studio/metrics/detection/__init__.py +0 -0
- lightly_studio/metrics/detection/map.py +0 -268
- lightly_studio/models/annotation_task.py +0 -28
- lightly_studio/resolvers/annotation_resolver/create.py +0 -19
- lightly_studio/resolvers/annotation_task_resolver.py +0 -31
- {lightly_studio-0.3.2.dist-info → lightly_studio-0.3.3.dist-info}/WHEEL +0 -0
lightly_studio/core/dataset.py
CHANGED
|
@@ -28,15 +28,15 @@ from lightly_studio.core.dataset_query.order_by import OrderByExpression
|
|
|
28
28
|
from lightly_studio.core.sample import Sample
|
|
29
29
|
from lightly_studio.dataset import fsspec_lister
|
|
30
30
|
from lightly_studio.dataset.embedding_manager import EmbeddingManagerProvider
|
|
31
|
-
from lightly_studio.
|
|
32
|
-
|
|
31
|
+
from lightly_studio.metadata import compute_typicality
|
|
32
|
+
from lightly_studio.models.annotation.annotation_base import (
|
|
33
33
|
AnnotationType,
|
|
34
34
|
)
|
|
35
35
|
from lightly_studio.models.dataset import DatasetCreate, DatasetTable
|
|
36
36
|
from lightly_studio.models.sample import SampleTable
|
|
37
37
|
from lightly_studio.resolvers import (
|
|
38
|
-
annotation_task_resolver,
|
|
39
38
|
dataset_resolver,
|
|
39
|
+
embedding_model_resolver,
|
|
40
40
|
sample_resolver,
|
|
41
41
|
)
|
|
42
42
|
from lightly_studio.type_definitions import PathLike
|
|
@@ -234,8 +234,6 @@ class Dataset:
|
|
|
234
234
|
self,
|
|
235
235
|
input_labels: ObjectDetectionInput | InstanceSegmentationInput,
|
|
236
236
|
images_path: PathLike,
|
|
237
|
-
is_prediction: bool = True,
|
|
238
|
-
task_name: str | None = None,
|
|
239
237
|
embed: bool = True,
|
|
240
238
|
) -> None:
|
|
241
239
|
"""Load a dataset from a labelformat object and store in database.
|
|
@@ -243,40 +241,17 @@ class Dataset:
|
|
|
243
241
|
Args:
|
|
244
242
|
input_labels: The labelformat input object.
|
|
245
243
|
images_path: Path to the folder containing the images.
|
|
246
|
-
is_prediction: Whether the task is for prediction or labels.
|
|
247
|
-
task_name: Optional name for the annotation task. If None, a
|
|
248
|
-
default name is generated.
|
|
249
244
|
embed: If True, generate embeddings for the newly added samples.
|
|
250
245
|
"""
|
|
251
246
|
if isinstance(images_path, str):
|
|
252
247
|
images_path = Path(images_path)
|
|
253
248
|
images_path = images_path.absolute()
|
|
254
249
|
|
|
255
|
-
# Determine annotation type based on input.
|
|
256
|
-
# Currently, we always create BBOX tasks, even for segmentation,
|
|
257
|
-
# as segmentation data is stored alongside bounding boxes.
|
|
258
|
-
annotation_type = AnnotationType.BBOX
|
|
259
|
-
|
|
260
|
-
# Generate a default task name if none is provided.
|
|
261
|
-
if task_name is None:
|
|
262
|
-
task_name = f"Loaded from labelformat: {self.name}"
|
|
263
|
-
|
|
264
|
-
# Create annotation task.
|
|
265
|
-
new_annotation_task = annotation_task_resolver.create(
|
|
266
|
-
session=self.session,
|
|
267
|
-
annotation_task=AnnotationTaskTable(
|
|
268
|
-
name=task_name,
|
|
269
|
-
annotation_type=annotation_type,
|
|
270
|
-
is_prediction=is_prediction,
|
|
271
|
-
),
|
|
272
|
-
)
|
|
273
|
-
|
|
274
250
|
created_sample_ids = add_samples.load_into_dataset_from_labelformat(
|
|
275
251
|
session=self.session,
|
|
276
252
|
dataset_id=self.dataset_id,
|
|
277
253
|
input_labels=input_labels,
|
|
278
254
|
images_path=images_path,
|
|
279
|
-
annotation_task_id=new_annotation_task.annotation_task_id,
|
|
280
255
|
)
|
|
281
256
|
|
|
282
257
|
if embed:
|
|
@@ -288,7 +263,6 @@ class Dataset:
|
|
|
288
263
|
self,
|
|
289
264
|
data_yaml: PathLike,
|
|
290
265
|
input_split: str = "train",
|
|
291
|
-
task_name: str | None = None,
|
|
292
266
|
embed: bool = True,
|
|
293
267
|
) -> None:
|
|
294
268
|
"""Load a dataset in YOLO format and store in DB.
|
|
@@ -296,8 +270,6 @@ class Dataset:
|
|
|
296
270
|
Args:
|
|
297
271
|
data_yaml: Path to the YOLO data.yaml file.
|
|
298
272
|
input_split: The split to load (e.g., 'train', 'val').
|
|
299
|
-
task_name: Optional name for the annotation task. If None, a
|
|
300
|
-
default name is generated.
|
|
301
273
|
embed: If True, generate embeddings for the newly added samples.
|
|
302
274
|
"""
|
|
303
275
|
if isinstance(data_yaml, str):
|
|
@@ -307,9 +279,6 @@ class Dataset:
|
|
|
307
279
|
if not data_yaml.is_file() or data_yaml.suffix != ".yaml":
|
|
308
280
|
raise FileNotFoundError(f"YOLO data yaml file not found: '{data_yaml}'")
|
|
309
281
|
|
|
310
|
-
if task_name is None:
|
|
311
|
-
task_name = f"Loaded from YOLO: {data_yaml.name} ({input_split} split)"
|
|
312
|
-
|
|
313
282
|
# Load the dataset using labelformat.
|
|
314
283
|
label_input = YOLOv8ObjectDetectionInput(
|
|
315
284
|
input_file=data_yaml,
|
|
@@ -320,8 +289,6 @@ class Dataset:
|
|
|
320
289
|
self.add_samples_from_labelformat(
|
|
321
290
|
input_labels=label_input,
|
|
322
291
|
images_path=images_path,
|
|
323
|
-
is_prediction=False,
|
|
324
|
-
task_name=task_name,
|
|
325
292
|
embed=embed,
|
|
326
293
|
)
|
|
327
294
|
|
|
@@ -329,8 +296,7 @@ class Dataset:
|
|
|
329
296
|
self,
|
|
330
297
|
annotations_json: PathLike,
|
|
331
298
|
images_path: PathLike,
|
|
332
|
-
|
|
333
|
-
annotation_type: AnnotationType = AnnotationType.BBOX,
|
|
299
|
+
annotation_type: AnnotationType = AnnotationType.OBJECT_DETECTION,
|
|
334
300
|
embed: bool = True,
|
|
335
301
|
) -> None:
|
|
336
302
|
"""Load a dataset in COCO Object Detection format and store in DB.
|
|
@@ -338,8 +304,6 @@ class Dataset:
|
|
|
338
304
|
Args:
|
|
339
305
|
annotations_json: Path to the COCO annotations JSON file.
|
|
340
306
|
images_path: Path to the folder containing the images.
|
|
341
|
-
task_name: Optional name for the annotation task. If None, a
|
|
342
|
-
default name is generated.
|
|
343
307
|
annotation_type: The type of annotation to be loaded (e.g., 'ObjectDetection',
|
|
344
308
|
'InstanceSegmentation').
|
|
345
309
|
embed: If True, generate embeddings for the newly added samples.
|
|
@@ -353,32 +317,52 @@ class Dataset:
|
|
|
353
317
|
|
|
354
318
|
label_input: COCOObjectDetectionInput | COCOInstanceSegmentationInput
|
|
355
319
|
|
|
356
|
-
if annotation_type == AnnotationType.
|
|
320
|
+
if annotation_type == AnnotationType.OBJECT_DETECTION:
|
|
357
321
|
label_input = COCOObjectDetectionInput(
|
|
358
322
|
input_file=annotations_json,
|
|
359
323
|
)
|
|
360
|
-
task_name_default = f"Loaded from COCO Object Detection: {annotations_json.name}"
|
|
361
324
|
elif annotation_type == AnnotationType.INSTANCE_SEGMENTATION:
|
|
362
325
|
label_input = COCOInstanceSegmentationInput(
|
|
363
326
|
input_file=annotations_json,
|
|
364
327
|
)
|
|
365
|
-
task_name_default = f"Loaded from COCO Instance Segmentation: {annotations_json.name}"
|
|
366
328
|
else:
|
|
367
329
|
raise ValueError(f"Invalid annotation type: {annotation_type}")
|
|
368
330
|
|
|
369
|
-
if task_name is None:
|
|
370
|
-
task_name = task_name_default
|
|
371
|
-
|
|
372
331
|
images_path = Path(images_path).absolute()
|
|
373
332
|
|
|
374
333
|
self.add_samples_from_labelformat(
|
|
375
334
|
input_labels=label_input,
|
|
376
335
|
images_path=images_path,
|
|
377
|
-
is_prediction=False,
|
|
378
|
-
task_name=task_name,
|
|
379
336
|
embed=embed,
|
|
380
337
|
)
|
|
381
338
|
|
|
339
|
+
def compute_typicality_metadata(
|
|
340
|
+
self,
|
|
341
|
+
embedding_model_name: str | None = None,
|
|
342
|
+
metadata_name: str = "typicality",
|
|
343
|
+
) -> None:
|
|
344
|
+
"""Computes typicality from embeddings, for K nearest neighbors.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
embedding_model_name:
|
|
348
|
+
The name of the embedding model to use. If not given, the default
|
|
349
|
+
embedding model is used.
|
|
350
|
+
metadata_name:
|
|
351
|
+
The name of the metadata to store the typicality values in. If not give, the default
|
|
352
|
+
name "typicality" is used.
|
|
353
|
+
"""
|
|
354
|
+
embedding_model_id = embedding_model_resolver.get_by_name(
|
|
355
|
+
session=self.session,
|
|
356
|
+
dataset_id=self.dataset_id,
|
|
357
|
+
embedding_model_name=embedding_model_name,
|
|
358
|
+
).embedding_model_id
|
|
359
|
+
compute_typicality.compute_typicality_metadata(
|
|
360
|
+
session=self.session,
|
|
361
|
+
dataset_id=self.dataset_id,
|
|
362
|
+
embedding_model_id=embedding_model_id,
|
|
363
|
+
metadata_name=metadata_name,
|
|
364
|
+
)
|
|
365
|
+
|
|
382
366
|
|
|
383
367
|
def _generate_embeddings(session: Session, dataset_id: UUID, sample_ids: list[UUID]) -> None:
|
|
384
368
|
"""Generate and store embeddings for samples.
|
|
@@ -10,6 +10,7 @@ from lightly_studio.core.dataset_query.match_expression import MatchExpression
|
|
|
10
10
|
from lightly_studio.core.dataset_query.order_by import OrderByExpression, OrderByField
|
|
11
11
|
from lightly_studio.core.dataset_query.sample_field import SampleField
|
|
12
12
|
from lightly_studio.core.sample import Sample
|
|
13
|
+
from lightly_studio.export.export_dataset import DatasetExport
|
|
13
14
|
from lightly_studio.models.dataset import DatasetTable
|
|
14
15
|
from lightly_studio.models.sample import SampleTable
|
|
15
16
|
from lightly_studio.resolvers import tag_resolver
|
|
@@ -209,3 +210,7 @@ class DatasetQuery:
|
|
|
209
210
|
session=self.session,
|
|
210
211
|
input_sample_ids=input_sample_ids,
|
|
211
212
|
)
|
|
213
|
+
|
|
214
|
+
def export(self) -> DatasetExport:
|
|
215
|
+
"""Return a DatasetExport instance which can export the dataset in various formats."""
|
|
216
|
+
return DatasetExport(session=self.session, samples=self)
|
lightly_studio/dataset/env.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Initialize environment variables for the dataset module."""
|
|
2
2
|
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
3
5
|
from environs import Env
|
|
4
6
|
|
|
5
7
|
env = Env()
|
|
@@ -14,3 +16,5 @@ LIGHTLY_STUDIO_HOST: str = env.str("LIGHTLY_STUDIO_HOST", "localhost")
|
|
|
14
16
|
LIGHTLY_STUDIO_DEBUG: str = env.bool("LIGHTLY_STUDIO_DEBUG", "false")
|
|
15
17
|
|
|
16
18
|
APP_URL = f"{LIGHTLY_STUDIO_PROTOCOL}://{LIGHTLY_STUDIO_HOST}:{LIGHTLY_STUDIO_PORT}"
|
|
19
|
+
|
|
20
|
+
LIGHTLY_STUDIO_LICENSE_KEY: Optional[str] = env.str("LIGHTLY_STUDIO_LICENSE_KEY", default=None)
|
|
@@ -13,8 +13,19 @@ def download_file_if_does_not_exist(url: str, local_filename: Path) -> None:
|
|
|
13
13
|
"""Download a file from a URL if it does not already exist locally."""
|
|
14
14
|
if local_filename.exists():
|
|
15
15
|
return
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
print(f"Downloading {url} to {local_filename}")
|
|
19
|
+
with requests.get(url, stream=True, timeout=30) as r:
|
|
20
|
+
# Raise an error for bad status codes
|
|
21
|
+
r.raise_for_status()
|
|
22
|
+
with open(local_filename, "wb") as f:
|
|
23
|
+
shutil.copyfileobj(r.raw, f)
|
|
24
|
+
except Exception:
|
|
25
|
+
# If download fails, remove any partial file to allow retry.
|
|
26
|
+
if local_filename.exists():
|
|
27
|
+
local_filename.unlink()
|
|
28
|
+
raise
|
|
18
29
|
|
|
19
30
|
|
|
20
31
|
def get_file_xxhash(file_path: Path) -> str:
|
lightly_studio/dataset/loader.py
CHANGED
|
@@ -41,16 +41,11 @@ from lightly_studio.dataset.embedding_manager import (
|
|
|
41
41
|
)
|
|
42
42
|
from lightly_studio.models.annotation.annotation_base import AnnotationCreate
|
|
43
43
|
from lightly_studio.models.annotation_label import AnnotationLabelCreate
|
|
44
|
-
from lightly_studio.models.annotation_task import (
|
|
45
|
-
AnnotationTaskTable,
|
|
46
|
-
AnnotationType,
|
|
47
|
-
)
|
|
48
44
|
from lightly_studio.models.dataset import DatasetCreate, DatasetTable
|
|
49
45
|
from lightly_studio.models.sample import SampleCreate, SampleTable
|
|
50
46
|
from lightly_studio.resolvers import (
|
|
51
47
|
annotation_label_resolver,
|
|
52
48
|
annotation_resolver,
|
|
53
|
-
annotation_task_resolver,
|
|
54
49
|
dataset_resolver,
|
|
55
50
|
sample_resolver,
|
|
56
51
|
)
|
|
@@ -67,7 +62,6 @@ class AnnotationProcessingContext:
|
|
|
67
62
|
dataset_id: UUID
|
|
68
63
|
sample_id: UUID
|
|
69
64
|
label_map: dict[int, UUID]
|
|
70
|
-
annotation_task_id: UUID
|
|
71
65
|
|
|
72
66
|
|
|
73
67
|
class DatasetLoader:
|
|
@@ -83,7 +77,6 @@ class DatasetLoader:
|
|
|
83
77
|
dataset: DatasetTable,
|
|
84
78
|
input_labels: ObjectDetectionInput | InstanceSegmentationInput,
|
|
85
79
|
img_dir: Path,
|
|
86
|
-
annotation_task_id: UUID,
|
|
87
80
|
) -> None:
|
|
88
81
|
"""Store a loaded dataset in database."""
|
|
89
82
|
# Create label mapping
|
|
@@ -120,7 +113,6 @@ class DatasetLoader:
|
|
|
120
113
|
samples_data=samples_image_data,
|
|
121
114
|
dataset_id=dataset.dataset_id,
|
|
122
115
|
label_map=label_map,
|
|
123
|
-
annotation_task_id=annotation_task_id,
|
|
124
116
|
annotations_to_create=annotations_to_create,
|
|
125
117
|
sample_ids=sample_ids,
|
|
126
118
|
)
|
|
@@ -137,7 +129,6 @@ class DatasetLoader:
|
|
|
137
129
|
samples_data=samples_image_data,
|
|
138
130
|
dataset_id=dataset.dataset_id,
|
|
139
131
|
label_map=label_map,
|
|
140
|
-
annotation_task_id=annotation_task_id,
|
|
141
132
|
annotations_to_create=annotations_to_create,
|
|
142
133
|
sample_ids=sample_ids,
|
|
143
134
|
)
|
|
@@ -188,23 +179,18 @@ class DatasetLoader:
|
|
|
188
179
|
input_labels=label_input,
|
|
189
180
|
dataset_name=dataset_name,
|
|
190
181
|
img_dir=str(img_dir),
|
|
191
|
-
is_prediction=False,
|
|
192
|
-
task_name=task_name,
|
|
193
182
|
)
|
|
194
183
|
|
|
195
184
|
def from_coco_object_detections(
|
|
196
185
|
self,
|
|
197
186
|
annotations_json_path: str,
|
|
198
187
|
img_dir: str,
|
|
199
|
-
task_name: str | None = None,
|
|
200
188
|
) -> DatasetTable:
|
|
201
189
|
"""Load a dataset in COCO Object Detection format and store in DB.
|
|
202
190
|
|
|
203
191
|
Args:
|
|
204
192
|
annotations_json_path: Path to the COCO annotations JSON file.
|
|
205
193
|
img_dir: Path to the folder containing the images.
|
|
206
|
-
task_name: Optional name for the annotation task. If None, a
|
|
207
|
-
default name is generated.
|
|
208
194
|
|
|
209
195
|
Returns:
|
|
210
196
|
DatasetTable: The created dataset table entry.
|
|
@@ -212,9 +198,6 @@ class DatasetLoader:
|
|
|
212
198
|
annotations_json = Path(annotations_json_path)
|
|
213
199
|
dataset_name = annotations_json.parent.name
|
|
214
200
|
|
|
215
|
-
if task_name is None:
|
|
216
|
-
task_name = f"Loaded from COCO Object Detection: {annotations_json.name}"
|
|
217
|
-
|
|
218
201
|
label_input = COCOObjectDetectionInput(
|
|
219
202
|
input_file=annotations_json,
|
|
220
203
|
)
|
|
@@ -224,23 +207,18 @@ class DatasetLoader:
|
|
|
224
207
|
input_labels=label_input,
|
|
225
208
|
dataset_name=dataset_name,
|
|
226
209
|
img_dir=str(img_dir_path),
|
|
227
|
-
is_prediction=False,
|
|
228
|
-
task_name=task_name,
|
|
229
210
|
)
|
|
230
211
|
|
|
231
212
|
def from_coco_instance_segmentations(
|
|
232
213
|
self,
|
|
233
214
|
annotations_json_path: str,
|
|
234
215
|
img_dir: str,
|
|
235
|
-
task_name: str | None = None,
|
|
236
216
|
) -> DatasetTable:
|
|
237
217
|
"""Load a dataset in COCO Instance Segmentation format and store in DB.
|
|
238
218
|
|
|
239
219
|
Args:
|
|
240
220
|
annotations_json_path: Path to the COCO annotations JSON file.
|
|
241
221
|
img_dir: Path to the folder containing the images.
|
|
242
|
-
task_name: Optional name for the annotation task. If None, a
|
|
243
|
-
default name is generated.
|
|
244
222
|
|
|
245
223
|
Returns:
|
|
246
224
|
DatasetTable: The created dataset table entry.
|
|
@@ -248,9 +226,6 @@ class DatasetLoader:
|
|
|
248
226
|
annotations_json = Path(annotations_json_path)
|
|
249
227
|
dataset_name = annotations_json.parent.name
|
|
250
228
|
|
|
251
|
-
if task_name is None:
|
|
252
|
-
task_name = f"Loaded from COCO Instance Segmentation: {annotations_json.name}"
|
|
253
|
-
|
|
254
229
|
label_input = COCOInstanceSegmentationInput(
|
|
255
230
|
input_file=annotations_json,
|
|
256
231
|
)
|
|
@@ -260,8 +235,6 @@ class DatasetLoader:
|
|
|
260
235
|
input_labels=label_input,
|
|
261
236
|
dataset_name=dataset_name,
|
|
262
237
|
img_dir=str(img_dir_path),
|
|
263
|
-
is_prediction=False,
|
|
264
|
-
task_name=task_name,
|
|
265
238
|
)
|
|
266
239
|
|
|
267
240
|
def from_labelformat(
|
|
@@ -269,8 +242,6 @@ class DatasetLoader:
|
|
|
269
242
|
input_labels: ObjectDetectionInput | InstanceSegmentationInput,
|
|
270
243
|
dataset_name: str,
|
|
271
244
|
img_dir: str,
|
|
272
|
-
is_prediction: bool = True,
|
|
273
|
-
task_name: str | None = None,
|
|
274
245
|
) -> DatasetTable:
|
|
275
246
|
"""Load a dataset from a labelformat object and store in database.
|
|
276
247
|
|
|
@@ -278,24 +249,12 @@ class DatasetLoader:
|
|
|
278
249
|
input_labels: The labelformat input object.
|
|
279
250
|
dataset_name: The name for the new dataset.
|
|
280
251
|
img_dir: Path to the folder containing the images.
|
|
281
|
-
is_prediction: Whether the task is for prediction or labels.
|
|
282
|
-
task_name: Optional name for the annotation task. If None, a
|
|
283
|
-
default name is generated.
|
|
284
252
|
|
|
285
253
|
Returns:
|
|
286
254
|
DatasetTable: The created dataset table entry.
|
|
287
255
|
"""
|
|
288
256
|
img_dir_path = Path(img_dir).absolute()
|
|
289
257
|
|
|
290
|
-
# Determine annotation type based on input.
|
|
291
|
-
# Currently, we always create BBOX tasks, even for segmentation,
|
|
292
|
-
# as segmentation data is stored alongside bounding boxes.
|
|
293
|
-
annotation_type = AnnotationType.BBOX
|
|
294
|
-
|
|
295
|
-
# Generate a default task name if none is provided.
|
|
296
|
-
if task_name is None:
|
|
297
|
-
task_name = f"Loaded from labelformat: {dataset_name}"
|
|
298
|
-
|
|
299
258
|
# Create dataset and annotation task.
|
|
300
259
|
dataset = dataset_resolver.create(
|
|
301
260
|
session=self.session,
|
|
@@ -304,20 +263,11 @@ class DatasetLoader:
|
|
|
304
263
|
directory=str(img_dir_path),
|
|
305
264
|
),
|
|
306
265
|
)
|
|
307
|
-
new_annotation_task = annotation_task_resolver.create(
|
|
308
|
-
session=self.session,
|
|
309
|
-
annotation_task=AnnotationTaskTable(
|
|
310
|
-
name=task_name,
|
|
311
|
-
annotation_type=annotation_type,
|
|
312
|
-
is_prediction=is_prediction,
|
|
313
|
-
),
|
|
314
|
-
)
|
|
315
266
|
|
|
316
267
|
self._load_into_dataset(
|
|
317
268
|
dataset=dataset,
|
|
318
269
|
input_labels=input_labels,
|
|
319
270
|
img_dir=img_dir_path,
|
|
320
|
-
annotation_task_id=new_annotation_task.annotation_task_id,
|
|
321
271
|
)
|
|
322
272
|
return dataset
|
|
323
273
|
|
|
@@ -501,7 +451,6 @@ def _process_object_detection_annotations(
|
|
|
501
451
|
width=int(width),
|
|
502
452
|
height=int(height),
|
|
503
453
|
confidence=obj.confidence,
|
|
504
|
-
annotation_task_id=context.annotation_task_id,
|
|
505
454
|
)
|
|
506
455
|
)
|
|
507
456
|
return new_annotations
|
|
@@ -536,7 +485,6 @@ def _process_instance_segmentation_annotations(
|
|
|
536
485
|
width=int(width),
|
|
537
486
|
height=int(height),
|
|
538
487
|
segmentation_mask=segmentation_rle,
|
|
539
|
-
annotation_task_id=context.annotation_task_id,
|
|
540
488
|
)
|
|
541
489
|
)
|
|
542
490
|
return new_annotations
|
|
@@ -548,7 +496,6 @@ def _process_batch_annotations( # noqa: PLR0913
|
|
|
548
496
|
samples_data: list[tuple[SampleCreate, ImageInstanceSegmentation | ImageObjectDetection]],
|
|
549
497
|
dataset_id: UUID,
|
|
550
498
|
label_map: dict[int, UUID],
|
|
551
|
-
annotation_task_id: UUID,
|
|
552
499
|
annotations_to_create: list[AnnotationCreate],
|
|
553
500
|
sample_ids: list[UUID],
|
|
554
501
|
) -> None:
|
|
@@ -560,7 +507,6 @@ def _process_batch_annotations( # noqa: PLR0913
|
|
|
560
507
|
dataset_id=dataset_id,
|
|
561
508
|
sample_id=stored_sample.sample_id,
|
|
562
509
|
label_map=label_map,
|
|
563
|
-
annotation_task_id=annotation_task_id,
|
|
564
510
|
)
|
|
565
511
|
|
|
566
512
|
if isinstance(img_data, ImageInstanceSegmentation):
|
|
@@ -24,6 +24,7 @@ MOBILECLIP_DOWNLOAD_URL = (
|
|
|
24
24
|
f"https://docs-assets.developer.apple.com/ml-research/datasets/mobileclip/{MODEL_NAME}.pt"
|
|
25
25
|
)
|
|
26
26
|
MAX_BATCH_SIZE: int = 16
|
|
27
|
+
EMBEDDING_DIMENSION: int = 512
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
# Dataset for efficient batched image loading and preprocessing
|
|
@@ -85,7 +86,7 @@ class MobileCLIPEmbeddingGenerator(EmbeddingGenerator):
|
|
|
85
86
|
return EmbeddingModelCreate(
|
|
86
87
|
name=MODEL_NAME,
|
|
87
88
|
embedding_model_hash=self._model_hash,
|
|
88
|
-
embedding_dimension=
|
|
89
|
+
embedding_dimension=EMBEDDING_DIMENSION,
|
|
89
90
|
dataset_id=dataset_id,
|
|
90
91
|
)
|
|
91
92
|
|
|
@@ -138,7 +139,7 @@ class MobileCLIPEmbeddingGenerator(EmbeddingGenerator):
|
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
def _get_cached_mobileclip_checkpoint() -> Path:
|
|
141
|
-
file_path = Path(tempfile.gettempdir()) / "
|
|
142
|
+
file_path = Path(tempfile.gettempdir()) / f"{MODEL_NAME}.pt"
|
|
142
143
|
file_utils.download_file_if_does_not_exist(
|
|
143
144
|
url=MOBILECLIP_DOWNLOAD_URL,
|
|
144
145
|
local_filename=file_path,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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%)}@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}.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-4{margin-left:1rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.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-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-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-\[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-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-1\/2{width:50%}.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-\[32px\]{width:32px}.w-\[375px\]{width:375px}.w-\[400px\]{width:400px}.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}.flex-grow,.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))}.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-crosshair{cursor:crosshair}.cursor-default{cursor:default}.cursor-grab{cursor:grab}.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}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.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)}.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\/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-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-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.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}.pb-4{padding-bottom:1rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.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-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-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-muted-foreground{--tw-text-opacity: 1;color:hsl(var(--muted-foreground) / 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-primary{--tw-ring-opacity: 1;--tw-ring-color: hsl(var(--primary) / 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-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-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-w-\[425px\]{max-width:425px}.sm\:max-w-\[500px\]{max-width:500px}.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}
|