yangdx commited on
Commit
66aece8
·
2 Parent(s): 879a20d 1c6f17d

Merge branch 'main' into add-multi-worker-support

Browse files
README.md CHANGED
@@ -3,7 +3,7 @@
3
  <table border="0" width="100%">
4
  <tr>
5
  <td width="100" align="center">
6
- <img src="https://i-blog.csdnimg.cn/direct/0d97ea81439442a19ac3972ad537a811.png" width="80" height="80" alt="lightrag">
7
  </td>
8
  <td>
9
  <div>
 
3
  <table border="0" width="100%">
4
  <tr>
5
  <td width="100" align="center">
6
+ <img src="./assets/logo.png" width="80" height="80" alt="lightrag">
7
  </td>
8
  <td>
9
  <div>
assets/logo.png CHANGED

Git LFS Details

  • SHA256: 1641755ef4156d94d7399d4fa55afec4fcefc779c0e4359a113aa0f182c4548a
  • Pointer size: 131 Bytes
  • Size of remote file: 189 kB

Git LFS Details

  • SHA256: 38f318052f4521251aafb98e9dde098630d4ac4ff3ef21045ef4e651a8e4926c
  • Pointer size: 131 Bytes
  • Size of remote file: 159 kB
lightrag/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
  from .lightrag import LightRAG as LightRAG, QueryParam as QueryParam
2
 
3
- __version__ = "1.2.1"
4
  __author__ = "Zirui Guo"
5
  __url__ = "https://github.com/HKUDS/LightRAG"
 
1
  from .lightrag import LightRAG as LightRAG, QueryParam as QueryParam
2
 
3
+ __version__ = "1.2.2"
4
  __author__ = "Zirui Guo"
5
  __url__ = "https://github.com/HKUDS/LightRAG"
lightrag/api/routers/graph_routes.py CHANGED
@@ -20,8 +20,8 @@ def create_graph_routes(rag, api_key: Optional[str] = None):
20
  return await rag.get_graph_labels()
21
 
22
  @router.get("/graphs", dependencies=[Depends(optional_api_key)])
23
- async def get_knowledge_graph(label: str):
24
  """Get knowledge graph for a specific label"""
25
- return await rag.get_knowledge_graph(node_label=label, max_depth=3)
26
 
27
  return router
 
20
  return await rag.get_graph_labels()
21
 
22
  @router.get("/graphs", dependencies=[Depends(optional_api_key)])
23
+ async def get_knowledge_graph(label: str, max_depth: int = 3):
24
  """Get knowledge graph for a specific label"""
25
+ return await rag.get_knowledge_graph(node_label=label, max_depth=max_depth)
26
 
27
  return router
lightrag/api/webui/assets/index-BDX8o1Ld.js DELETED
Binary file (994 kB)
 
lightrag/api/webui/assets/index-CLsJV-0i.css DELETED
Binary file (63.9 kB)
 
lightrag/api/webui/assets/index-DbuMPJAD.js ADDED
Binary file (1.8 MB). View file
 
lightrag/api/webui/assets/index-rP-YlyR1.css ADDED
Binary file (47.5 kB). View file
 
lightrag/api/webui/index.html CHANGED
Binary files a/lightrag/api/webui/index.html and b/lightrag/api/webui/index.html differ
 
lightrag/api/webui/logo.png ADDED

Git LFS Details

  • SHA256: 38f318052f4521251aafb98e9dde098630d4ac4ff3ef21045ef4e651a8e4926c
  • Pointer size: 131 Bytes
  • Size of remote file: 159 kB
lightrag/api/webui/vite.svg DELETED
Binary file (1.5 kB)
 
lightrag/lightrag.py CHANGED
@@ -363,14 +363,14 @@ class LightRAG:
363
  self.namespace_prefix, NameSpace.VECTOR_STORE_ENTITIES
364
  ),
365
  embedding_func=self.embedding_func,
366
- meta_fields={"entity_name"},
367
  )
368
  self.relationships_vdb: BaseVectorStorage = self.vector_db_storage_cls( # type: ignore
369
  namespace=make_namespace(
370
  self.namespace_prefix, NameSpace.VECTOR_STORE_RELATIONSHIPS
371
  ),
372
  embedding_func=self.embedding_func,
373
- meta_fields={"src_id", "tgt_id"},
374
  )
375
  self.chunks_vdb: BaseVectorStorage = self.vector_db_storage_cls( # type: ignore
376
  namespace=make_namespace(
@@ -408,16 +408,31 @@ class LightRAG:
408
 
409
  self._storages_status = StoragesStatus.CREATED
410
 
411
- # Initialize storages
412
  if self.auto_manage_storages_states:
413
- loop = always_get_an_event_loop()
414
- loop.run_until_complete(self.initialize_storages())
415
 
416
  def __del__(self):
417
- # Finalize storages
418
  if self.auto_manage_storages_states:
 
 
 
 
 
419
  loop = always_get_an_event_loop()
420
- loop.run_until_complete(self.finalize_storages())
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
  async def initialize_storages(self):
423
  """Asynchronously initialize the storages"""
@@ -491,7 +506,7 @@ class LightRAG:
491
  input: str | list[str],
492
  split_by_character: str | None = None,
493
  split_by_character_only: bool = False,
494
- ids: list[str] | None = None,
495
  ) -> None:
496
  """Sync Insert documents with checkpoint support
497
 
@@ -500,7 +515,7 @@ class LightRAG:
500
  split_by_character: if split_by_character is not None, split the string by character, if chunk longer than
501
  split_by_character_only: if split_by_character_only is True, split the string by character only, when
502
  split_by_character is None, this parameter is ignored.
503
- ids: list of unique document IDs, if not provided, MD5 hash IDs will be generated
504
  """
505
  loop = always_get_an_event_loop()
506
  loop.run_until_complete(
@@ -512,7 +527,7 @@ class LightRAG:
512
  input: str | list[str],
513
  split_by_character: str | None = None,
514
  split_by_character_only: bool = False,
515
- ids: list[str] | None = None,
516
  ) -> None:
517
  """Async Insert documents with checkpoint support
518
 
@@ -528,12 +543,19 @@ class LightRAG:
528
  split_by_character, split_by_character_only
529
  )
530
 
531
- def insert_custom_chunks(self, full_text: str, text_chunks: list[str]) -> None:
 
 
 
 
 
532
  loop = always_get_an_event_loop()
533
- loop.run_until_complete(self.ainsert_custom_chunks(full_text, text_chunks))
 
 
534
 
535
  async def ainsert_custom_chunks(
536
- self, full_text: str, text_chunks: list[str]
537
  ) -> None:
538
  update_storage = False
539
  try:
@@ -542,7 +564,10 @@ class LightRAG:
542
  text_chunks = [self.clean_text(chunk) for chunk in text_chunks]
543
 
544
  # Process cleaned texts
545
- doc_key = compute_mdhash_id(full_text, prefix="doc-")
 
 
 
546
  new_docs = {doc_key: {"content": full_text}}
547
 
548
  _add_doc_keys = await self.full_docs.filter_keys({doc_key})
@@ -598,6 +623,8 @@ class LightRAG:
598
  """
599
  if isinstance(input, str):
600
  input = [input]
 
 
601
 
602
  # 1. Validate ids if provided or generate MD5 hash IDs
603
  if ids is not None:
@@ -1366,12 +1393,14 @@ class LightRAG:
1366
 
1367
  logger.debug(f"Starting deletion for document {doc_id}")
1368
 
 
 
1369
  # 2. Get all related chunks
1370
- chunks = await self.text_chunks.get_by_id(doc_id)
1371
  if not chunks:
1372
  return
1373
 
1374
- chunk_ids = list(chunks.keys())
1375
  logger.debug(f"Found {len(chunk_ids)} chunks to delete")
1376
 
1377
  # 3. Before deleting, check the related entities and relationships for these chunks
@@ -1380,7 +1409,7 @@ class LightRAG:
1380
  entities = [
1381
  dp
1382
  for dp in self.entities_vdb.client_storage["data"]
1383
- if dp.get("source_id") == chunk_id
1384
  ]
1385
  logger.debug(f"Chunk {chunk_id} has {len(entities)} related entities")
1386
 
@@ -1388,7 +1417,7 @@ class LightRAG:
1388
  relations = [
1389
  dp
1390
  for dp in self.relationships_vdb.client_storage["data"]
1391
- if dp.get("source_id") == chunk_id
1392
  ]
1393
  logger.debug(f"Chunk {chunk_id} has {len(relations)} related relations")
1394
 
@@ -1499,42 +1528,71 @@ class LightRAG:
1499
  f"Updated {len(entities_to_update)} entities and {len(relationships_to_update)} relationships."
1500
  )
1501
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1502
  # Add verification step
1503
  async def verify_deletion():
1504
  # Verify if the document has been deleted
1505
  if await self.full_docs.get_by_id(doc_id):
1506
- logger.error(f"Document {doc_id} still exists in full_docs")
1507
 
1508
  # Verify if chunks have been deleted
1509
- remaining_chunks = await self.text_chunks.get_by_id(doc_id)
1510
  if remaining_chunks:
1511
- logger.error(f"Found {len(remaining_chunks)} remaining chunks")
1512
 
1513
  # Verify entities and relationships
1514
  for chunk_id in chunk_ids:
1515
- # Check entities
1516
- entities_with_chunk = [
1517
- dp
1518
- for dp in self.entities_vdb.client_storage["data"]
1519
- if chunk_id
1520
- in (dp.get("source_id") or "").split(GRAPH_FIELD_SEP)
1521
- ]
1522
- if entities_with_chunk:
1523
- logger.error(
1524
- f"Found {len(entities_with_chunk)} entities still referencing chunk {chunk_id}"
1525
- )
1526
-
1527
- # Check relationships
1528
- relations_with_chunk = [
1529
- dp
1530
- for dp in self.relationships_vdb.client_storage["data"]
1531
- if chunk_id
1532
- in (dp.get("source_id") or "").split(GRAPH_FIELD_SEP)
1533
- ]
1534
- if relations_with_chunk:
1535
- logger.error(
1536
- f"Found {len(relations_with_chunk)} relations still referencing chunk {chunk_id}"
1537
- )
1538
 
1539
  await verify_deletion()
1540
 
 
363
  self.namespace_prefix, NameSpace.VECTOR_STORE_ENTITIES
364
  ),
365
  embedding_func=self.embedding_func,
366
+ meta_fields={"entity_name", "source_id", "content"},
367
  )
368
  self.relationships_vdb: BaseVectorStorage = self.vector_db_storage_cls( # type: ignore
369
  namespace=make_namespace(
370
  self.namespace_prefix, NameSpace.VECTOR_STORE_RELATIONSHIPS
371
  ),
372
  embedding_func=self.embedding_func,
373
+ meta_fields={"src_id", "tgt_id", "source_id", "content"},
374
  )
375
  self.chunks_vdb: BaseVectorStorage = self.vector_db_storage_cls( # type: ignore
376
  namespace=make_namespace(
 
408
 
409
  self._storages_status = StoragesStatus.CREATED
410
 
 
411
  if self.auto_manage_storages_states:
412
+ self._run_async_safely(self.initialize_storages, "Storage Initialization")
 
413
 
414
  def __del__(self):
 
415
  if self.auto_manage_storages_states:
416
+ self._run_async_safely(self.finalize_storages, "Storage Finalization")
417
+
418
+ def _run_async_safely(self, async_func, action_name=""):
419
+ """Safely execute an async function, avoiding event loop conflicts."""
420
+ try:
421
  loop = always_get_an_event_loop()
422
+ if loop.is_running():
423
+ task = loop.create_task(async_func())
424
+ task.add_done_callback(
425
+ lambda t: logger.info(f"{action_name} completed!")
426
+ )
427
+ else:
428
+ loop.run_until_complete(async_func())
429
+ except RuntimeError:
430
+ logger.warning(
431
+ f"No running event loop, creating a new loop for {action_name}."
432
+ )
433
+ loop = asyncio.new_event_loop()
434
+ loop.run_until_complete(async_func())
435
+ loop.close()
436
 
437
  async def initialize_storages(self):
438
  """Asynchronously initialize the storages"""
 
506
  input: str | list[str],
507
  split_by_character: str | None = None,
508
  split_by_character_only: bool = False,
509
+ ids: str | list[str] | None = None,
510
  ) -> None:
511
  """Sync Insert documents with checkpoint support
512
 
 
515
  split_by_character: if split_by_character is not None, split the string by character, if chunk longer than
516
  split_by_character_only: if split_by_character_only is True, split the string by character only, when
517
  split_by_character is None, this parameter is ignored.
518
+ ids: single string of the document ID or list of unique document IDs, if not provided, MD5 hash IDs will be generated
519
  """
520
  loop = always_get_an_event_loop()
521
  loop.run_until_complete(
 
527
  input: str | list[str],
528
  split_by_character: str | None = None,
529
  split_by_character_only: bool = False,
530
+ ids: str | list[str] | None = None,
531
  ) -> None:
532
  """Async Insert documents with checkpoint support
533
 
 
543
  split_by_character, split_by_character_only
544
  )
545
 
546
+ def insert_custom_chunks(
547
+ self,
548
+ full_text: str,
549
+ text_chunks: list[str],
550
+ doc_id: str | list[str] | None = None,
551
+ ) -> None:
552
  loop = always_get_an_event_loop()
553
+ loop.run_until_complete(
554
+ self.ainsert_custom_chunks(full_text, text_chunks, doc_id)
555
+ )
556
 
557
  async def ainsert_custom_chunks(
558
+ self, full_text: str, text_chunks: list[str], doc_id: str | None = None
559
  ) -> None:
560
  update_storage = False
561
  try:
 
564
  text_chunks = [self.clean_text(chunk) for chunk in text_chunks]
565
 
566
  # Process cleaned texts
567
+ if doc_id is None:
568
+ doc_key = compute_mdhash_id(full_text, prefix="doc-")
569
+ else:
570
+ doc_key = doc_id
571
  new_docs = {doc_key: {"content": full_text}}
572
 
573
  _add_doc_keys = await self.full_docs.filter_keys({doc_key})
 
623
  """
624
  if isinstance(input, str):
625
  input = [input]
626
+ if isinstance(ids, str):
627
+ ids = [ids]
628
 
629
  # 1. Validate ids if provided or generate MD5 hash IDs
630
  if ids is not None:
 
1393
 
1394
  logger.debug(f"Starting deletion for document {doc_id}")
1395
 
1396
+ doc_to_chunk_id = doc_id.replace("doc", "chunk")
1397
+
1398
  # 2. Get all related chunks
1399
+ chunks = await self.text_chunks.get_by_id(doc_to_chunk_id)
1400
  if not chunks:
1401
  return
1402
 
1403
+ chunk_ids = {chunks["full_doc_id"].replace("doc", "chunk")}
1404
  logger.debug(f"Found {len(chunk_ids)} chunks to delete")
1405
 
1406
  # 3. Before deleting, check the related entities and relationships for these chunks
 
1409
  entities = [
1410
  dp
1411
  for dp in self.entities_vdb.client_storage["data"]
1412
+ if chunk_id in dp.get("source_id")
1413
  ]
1414
  logger.debug(f"Chunk {chunk_id} has {len(entities)} related entities")
1415
 
 
1417
  relations = [
1418
  dp
1419
  for dp in self.relationships_vdb.client_storage["data"]
1420
+ if chunk_id in dp.get("source_id")
1421
  ]
1422
  logger.debug(f"Chunk {chunk_id} has {len(relations)} related relations")
1423
 
 
1528
  f"Updated {len(entities_to_update)} entities and {len(relationships_to_update)} relationships."
1529
  )
1530
 
1531
+ async def process_data(data_type, vdb, chunk_id):
1532
+ # Check data (entities or relationships)
1533
+ data_with_chunk = [
1534
+ dp
1535
+ for dp in vdb.client_storage["data"]
1536
+ if chunk_id in (dp.get("source_id") or "").split(GRAPH_FIELD_SEP)
1537
+ ]
1538
+
1539
+ data_for_vdb = {}
1540
+ if data_with_chunk:
1541
+ logger.warning(
1542
+ f"found {len(data_with_chunk)} {data_type} still referencing chunk {chunk_id}"
1543
+ )
1544
+
1545
+ for item in data_with_chunk:
1546
+ old_sources = item["source_id"].split(GRAPH_FIELD_SEP)
1547
+ new_sources = [src for src in old_sources if src != chunk_id]
1548
+
1549
+ if not new_sources:
1550
+ logger.info(
1551
+ f"{data_type} {item.get('entity_name', 'N/A')} is deleted because source_id is not exists"
1552
+ )
1553
+ await vdb.delete_entity(item)
1554
+ else:
1555
+ item["source_id"] = GRAPH_FIELD_SEP.join(new_sources)
1556
+ item_id = item["__id__"]
1557
+ data_for_vdb[item_id] = item.copy()
1558
+ if data_type == "entities":
1559
+ data_for_vdb[item_id]["content"] = data_for_vdb[
1560
+ item_id
1561
+ ].get("content") or (
1562
+ item.get("entity_name", "")
1563
+ + (item.get("description") or "")
1564
+ )
1565
+ else: # relationships
1566
+ data_for_vdb[item_id]["content"] = data_for_vdb[
1567
+ item_id
1568
+ ].get("content") or (
1569
+ (item.get("keywords") or "")
1570
+ + (item.get("src_id") or "")
1571
+ + (item.get("tgt_id") or "")
1572
+ + (item.get("description") or "")
1573
+ )
1574
+
1575
+ if data_for_vdb:
1576
+ await vdb.upsert(data_for_vdb)
1577
+ logger.info(f"Successfully updated {data_type} in vector DB")
1578
+
1579
  # Add verification step
1580
  async def verify_deletion():
1581
  # Verify if the document has been deleted
1582
  if await self.full_docs.get_by_id(doc_id):
1583
+ logger.warning(f"Document {doc_id} still exists in full_docs")
1584
 
1585
  # Verify if chunks have been deleted
1586
+ remaining_chunks = await self.text_chunks.get_by_id(doc_to_chunk_id)
1587
  if remaining_chunks:
1588
+ logger.warning(f"Found {len(remaining_chunks)} remaining chunks")
1589
 
1590
  # Verify entities and relationships
1591
  for chunk_id in chunk_ids:
1592
+ await process_data("entities", self.entities_vdb, chunk_id)
1593
+ await process_data(
1594
+ "relationships", self.relationships_vdb, chunk_id
1595
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1596
 
1597
  await verify_deletion()
1598
 
lightrag/operate.py CHANGED
@@ -323,6 +323,7 @@ async def _merge_edges_then_upsert(
323
  tgt_id=tgt_id,
324
  description=description,
325
  keywords=keywords,
 
326
  )
327
 
328
  return edge_data
@@ -365,7 +366,7 @@ async def extract_entities(
365
  tuple_delimiter=PROMPTS["DEFAULT_TUPLE_DELIMITER"],
366
  record_delimiter=PROMPTS["DEFAULT_RECORD_DELIMITER"],
367
  completion_delimiter=PROMPTS["DEFAULT_COMPLETION_DELIMITER"],
368
- entity_types=",".join(entity_types),
369
  language=language,
370
  )
371
  # add example's format
@@ -562,6 +563,7 @@ async def extract_entities(
562
  compute_mdhash_id(dp["entity_name"], prefix="ent-"): {
563
  "content": dp["entity_name"] + dp["description"],
564
  "entity_name": dp["entity_name"],
 
565
  }
566
  for dp in all_entities_data
567
  }
@@ -572,6 +574,7 @@ async def extract_entities(
572
  compute_mdhash_id(dp["src_id"] + dp["tgt_id"], prefix="rel-"): {
573
  "src_id": dp["src_id"],
574
  "tgt_id": dp["tgt_id"],
 
575
  "content": dp["keywords"]
576
  + dp["src_id"]
577
  + dp["tgt_id"]
@@ -595,7 +598,7 @@ async def kg_query(
595
  global_config: dict[str, str],
596
  hashing_kv: BaseKVStorage | None = None,
597
  system_prompt: str | None = None,
598
- ) -> str:
599
  # Handle cache
600
  use_model_func = global_config["llm_model_func"]
601
  args_hash = compute_args_hash(query_param.mode, query, cache_type="query")
@@ -1127,7 +1130,7 @@ async def _get_node_data(
1127
  len_node_datas = len(node_datas)
1128
  node_datas = truncate_list_by_token_size(
1129
  node_datas,
1130
- key=lambda x: x["description"],
1131
  max_token_size=query_param.max_token_for_local_context,
1132
  )
1133
  logger.debug(
@@ -1310,7 +1313,7 @@ async def _find_most_related_edges_from_entities(
1310
  )
1311
  all_edges_data = truncate_list_by_token_size(
1312
  all_edges_data,
1313
- key=lambda x: x["description"],
1314
  max_token_size=query_param.max_token_for_global_context,
1315
  )
1316
 
@@ -1364,7 +1367,7 @@ async def _get_edge_data(
1364
  )
1365
  edge_datas = truncate_list_by_token_size(
1366
  edge_datas,
1367
- key=lambda x: x["description"],
1368
  max_token_size=query_param.max_token_for_global_context,
1369
  )
1370
  use_entities, use_text_units = await asyncio.gather(
@@ -1468,7 +1471,7 @@ async def _find_most_related_entities_from_relationships(
1468
  len_node_datas = len(node_datas)
1469
  node_datas = truncate_list_by_token_size(
1470
  node_datas,
1471
- key=lambda x: x["description"],
1472
  max_token_size=query_param.max_token_for_local_context,
1473
  )
1474
  logger.debug(
 
323
  tgt_id=tgt_id,
324
  description=description,
325
  keywords=keywords,
326
+ source_id=source_id,
327
  )
328
 
329
  return edge_data
 
366
  tuple_delimiter=PROMPTS["DEFAULT_TUPLE_DELIMITER"],
367
  record_delimiter=PROMPTS["DEFAULT_RECORD_DELIMITER"],
368
  completion_delimiter=PROMPTS["DEFAULT_COMPLETION_DELIMITER"],
369
+ entity_types=", ".join(entity_types),
370
  language=language,
371
  )
372
  # add example's format
 
563
  compute_mdhash_id(dp["entity_name"], prefix="ent-"): {
564
  "content": dp["entity_name"] + dp["description"],
565
  "entity_name": dp["entity_name"],
566
+ "source_id": dp["source_id"],
567
  }
568
  for dp in all_entities_data
569
  }
 
574
  compute_mdhash_id(dp["src_id"] + dp["tgt_id"], prefix="rel-"): {
575
  "src_id": dp["src_id"],
576
  "tgt_id": dp["tgt_id"],
577
+ "source_id": dp["source_id"],
578
  "content": dp["keywords"]
579
  + dp["src_id"]
580
  + dp["tgt_id"]
 
598
  global_config: dict[str, str],
599
  hashing_kv: BaseKVStorage | None = None,
600
  system_prompt: str | None = None,
601
+ ) -> str | AsyncIterator[str]:
602
  # Handle cache
603
  use_model_func = global_config["llm_model_func"]
604
  args_hash = compute_args_hash(query_param.mode, query, cache_type="query")
 
1130
  len_node_datas = len(node_datas)
1131
  node_datas = truncate_list_by_token_size(
1132
  node_datas,
1133
+ key=lambda x: x["description"] if x["description"] is not None else "",
1134
  max_token_size=query_param.max_token_for_local_context,
1135
  )
1136
  logger.debug(
 
1313
  )
1314
  all_edges_data = truncate_list_by_token_size(
1315
  all_edges_data,
1316
+ key=lambda x: x["description"] if x["description"] is not None else "",
1317
  max_token_size=query_param.max_token_for_global_context,
1318
  )
1319
 
 
1367
  )
1368
  edge_datas = truncate_list_by_token_size(
1369
  edge_datas,
1370
+ key=lambda x: x["description"] if x["description"] is not None else "",
1371
  max_token_size=query_param.max_token_for_global_context,
1372
  )
1373
  use_entities, use_text_units = await asyncio.gather(
 
1471
  len_node_datas = len(node_datas)
1472
  node_datas = truncate_list_by_token_size(
1473
  node_datas,
1474
+ key=lambda x: x["description"] if x["description"] is not None else "",
1475
  max_token_size=query_param.max_token_for_local_context,
1476
  )
1477
  logger.debug(
lightrag/prompt.py CHANGED
@@ -47,8 +47,9 @@ Format the content-level key words as ("content_keywords"{tuple_delimiter}<high_
47
  #############################
48
  ---Real Data---
49
  ######################
50
- Entity_types: {entity_types}
51
- Text: {input_text}
 
52
  ######################
53
  Output:"""
54
 
 
47
  #############################
48
  ---Real Data---
49
  ######################
50
+ Entity_types: [{entity_types}]
51
+ Text:
52
+ {input_text}
53
  ######################
54
  Output:"""
55
 
lightrag_webui/bun.lock CHANGED
@@ -35,46 +35,48 @@
35
  "graphology": "^0.26.0",
36
  "graphology-generators": "^0.11.2",
37
  "lucide-react": "^0.475.0",
38
- "minisearch": "^7.1.1",
39
  "react": "^19.0.0",
40
  "react-dom": "^19.0.0",
41
- "react-dropzone": "^14.3.5",
42
- "react-markdown": "^9.0.3",
43
  "react-number-format": "^5.4.3",
 
44
  "rehype-react": "^8.0.0",
45
  "remark-gfm": "^4.0.1",
46
  "remark-math": "^6.0.0",
47
  "seedrandom": "^3.0.5",
48
  "sigma": "^3.0.1",
49
  "sonner": "^1.7.4",
50
- "tailwind-merge": "^3.0.1",
51
- "tailwind-scrollbar": "^4.0.0",
52
  "zustand": "^5.0.3",
53
  },
54
  "devDependencies": {
55
- "@eslint/js": "^9.20.0",
56
  "@stylistic/eslint-plugin-js": "^3.1.0",
57
- "@tailwindcss/vite": "^4.0.6",
58
- "@types/bun": "^1.2.2",
59
- "@types/node": "^22.13.4",
60
  "@types/react": "^19.0.10",
61
  "@types/react-dom": "^19.0.4",
 
62
  "@types/seedrandom": "^3.0.8",
63
  "@vitejs/plugin-react-swc": "^3.8.0",
64
- "eslint": "^9.20.1",
65
  "eslint-config-prettier": "^10.0.1",
66
  "eslint-plugin-react": "^7.37.4",
67
  "eslint-plugin-react-hooks": "^5.1.0",
68
  "eslint-plugin-react-refresh": "^0.4.19",
69
  "globals": "^15.15.0",
70
  "graphology-types": "^0.24.8",
71
- "prettier": "^3.5.1",
72
  "prettier-plugin-tailwindcss": "^0.6.11",
73
- "tailwindcss": "^4.0.6",
74
  "tailwindcss-animate": "^1.0.7",
75
  "typescript": "~5.7.3",
76
- "typescript-eslint": "^8.24.0",
77
- "vite": "^6.1.0",
78
  },
79
  },
80
  },
@@ -177,15 +179,15 @@
177
 
178
  "@eslint/config-array": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
179
 
180
- "@eslint/core": ["@eslint/core@0.11.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA=="],
181
 
182
- "@eslint/eslintrc": ["@eslint/eslintrc@3.2.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w=="],
183
 
184
- "@eslint/js": ["@eslint/js@9.20.0", "", {}, "sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ=="],
185
 
186
  "@eslint/object-schema": ["@eslint/[email protected]", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
187
 
188
- "@eslint/plugin-kit": ["@eslint/[email protected].5", "", { "dependencies": { "@eslint/core": "^0.10.0", "levn": "^0.4.1" } }, "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A=="],
189
 
190
  "@faker-js/faker": ["@faker-js/[email protected]", "", {}, "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw=="],
191
 
@@ -203,7 +205,7 @@
203
 
204
  "@humanwhocodes/module-importer": ["@humanwhocodes/[email protected]", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
205
 
206
- "@humanwhocodes/retry": ["@humanwhocodes/[email protected].1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
207
 
208
  "@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
209
 
@@ -383,35 +385,35 @@
383
 
384
  "@swc/types": ["@swc/[email protected]", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ=="],
385
 
386
- "@tailwindcss/node": ["@tailwindcss/[email protected].6", "", { "dependencies": { "enhanced-resolve": "^5.18.0", "jiti": "^2.4.2", "tailwindcss": "4.0.6" } }, "sha512-jb6E0WeSq7OQbVYcIJ6LxnZTeC4HjMvbzFBMCrQff4R50HBlo/obmYNk6V2GCUXDeqiXtvtrQgcIbT+/boB03Q=="],
387
 
388
- "@tailwindcss/oxide": ["@tailwindcss/[email protected].6", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.0.6", "@tailwindcss/oxide-darwin-arm64": "4.0.6", "@tailwindcss/oxide-darwin-x64": "4.0.6", "@tailwindcss/oxide-freebsd-x64": "4.0.6", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.6", "@tailwindcss/oxide-linux-arm64-gnu": "4.0.6", "@tailwindcss/oxide-linux-arm64-musl": "4.0.6", "@tailwindcss/oxide-linux-x64-gnu": "4.0.6", "@tailwindcss/oxide-linux-x64-musl": "4.0.6", "@tailwindcss/oxide-win32-arm64-msvc": "4.0.6", "@tailwindcss/oxide-win32-x64-msvc": "4.0.6" } }, "sha512-lVyKV2y58UE9CeKVcYykULe9QaE1dtKdxDEdrTPIdbzRgBk6bdxHNAoDqvcqXbIGXubn3VOl1O/CFF77v/EqSA=="],
389
 
390
- "@tailwindcss/oxide-android-arm64": ["@tailwindcss/[email protected].6", "", { "os": "android", "cpu": "arm64" }, "sha512-xDbym6bDPW3D2XqQqX3PjqW3CKGe1KXH7Fdkc60sX5ZLVUbzPkFeunQaoP+BuYlLc2cC1FoClrIRYnRzof9Sow=="],
391
 
392
- "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/[email protected].6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1f71/ju/tvyGl5c2bDkchZHy8p8EK/tDHCxlpYJ1hGNvsYihZNurxVpZ0DefpN7cNc9RTT8DjrRoV8xXZKKRjg=="],
393
 
394
- "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/[email protected].6", "", { "os": "darwin", "cpu": "x64" }, "sha512-s/hg/ZPgxFIrGMb0kqyeaqZt505P891buUkSezmrDY6lxv2ixIELAlOcUVTkVh245SeaeEiUVUPiUN37cwoL2g=="],
395
 
396
- "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/[email protected].6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Z3Wo8FWZnmio8+xlcbb7JUo/hqRMSmhQw8IGIRoRJ7GmLR0C+25Wq+bEX/135xe/yEle2lFkhu9JBHd4wZYiig=="],
397
 
398
- "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/[email protected].6", "", { "os": "linux", "cpu": "arm" }, "sha512-SNSwkkim1myAgmnbHs4EjXsPL7rQbVGtjcok5EaIzkHkCAVK9QBQsWeP2Jm2/JJhq4wdx8tZB9Y7psMzHYWCkA=="],
399
 
400
- "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/[email protected].6", "", { "os": "linux", "cpu": "arm64" }, "sha512-tJ+mevtSDMQhKlwCCuhsFEFg058kBiSy4TkoeBG921EfrHKmexOaCyFKYhVXy4JtkaeeOcjJnCLasEeqml4i+Q=="],
401
 
402
- "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/[email protected].6", "", { "os": "linux", "cpu": "arm64" }, "sha512-IoArz1vfuTR4rALXMUXI/GWWfx2EaO4gFNtBNkDNOYhlTD4NVEwE45nbBoojYiTulajI4c2XH8UmVEVJTOJKxA=="],
403
 
404
- "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/[email protected].6", "", { "os": "linux", "cpu": "x64" }, "sha512-QtsUfLkEAeWAC3Owx9Kg+7JdzE+k9drPhwTAXbXugYB9RZUnEWWx5x3q/au6TvUYcL+n0RBqDEO2gucZRvRFgQ=="],
405
 
406
- "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/[email protected].6", "", { "os": "linux", "cpu": "x64" }, "sha512-QthvJqIji2KlGNwLcK/PPYo7w1Wsi/8NK0wAtRGbv4eOPdZHkQ9KUk+oCoP20oPO7i2a6X1aBAFQEL7i08nNMA=="],
407
 
408
- "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/[email protected].6", "", { "os": "win32", "cpu": "arm64" }, "sha512-+oka+dYX8jy9iP00DJ9Y100XsqvbqR5s0yfMZJuPR1H/lDVtDfsZiSix1UFBQ3X1HWxoEEl6iXNJHWd56TocVw=="],
409
 
410
- "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/[email protected].6", "", { "os": "win32", "cpu": "x64" }, "sha512-+o+juAkik4p8Ue/0LiflQXPmVatl6Av3LEZXpBTfg4qkMIbZdhCGWFzHdt2NjoMiLOJCFDddoV6GYaimvK1Olw=="],
411
 
412
- "@tailwindcss/vite": ["@tailwindcss/[email protected].6", "", { "dependencies": { "@tailwindcss/node": "^4.0.6", "@tailwindcss/oxide": "^4.0.6", "lightningcss": "^1.29.1", "tailwindcss": "4.0.6" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-O25vZ/URWbZ2JHdk2o8wH7jOKqEGCsYmX3GwGmYS5DjE4X3mpf93a72Rn7VRnefldNauBzr5z2hfZptmBNtTUQ=="],
413
 
414
- "@types/bun": ["@types/[email protected].2", "", { "dependencies": { "bun-types": "1.2.2" } }, "sha512-tr74gdku+AEDN5ergNiBnplr7hpDp3V1h7fqI2GcR/rsUaM39jpSeKH0TFibRvU0KwniRx5POgaYnaXbk0hU+w=="],
415
 
416
  "@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
417
 
@@ -429,7 +431,7 @@
429
 
430
  "@types/ms": ["@types/[email protected]", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
431
 
432
- "@types/node": ["@types/[email protected].4", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg=="],
433
 
434
  "@types/parse-json": ["@types/[email protected]", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
435
 
@@ -439,6 +441,8 @@
439
 
440
  "@types/react-dom": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg=="],
441
 
 
 
442
  "@types/react-transition-group": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
443
 
444
  "@types/seedrandom": ["@types/[email protected]", "", {}, "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ=="],
@@ -447,21 +451,21 @@
447
 
448
  "@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
449
 
450
- "@typescript-eslint/eslint-plugin": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.24.0", "@typescript-eslint/type-utils": "8.24.0", "@typescript-eslint/utils": "8.24.0", "@typescript-eslint/visitor-keys": "8.24.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-aFcXEJJCI4gUdXgoo/j9udUYIHgF23MFkg09LFz2dzEmU0+1Plk4rQWv/IYKvPHAtlkkGoB3m5e6oUp+JPsNaQ=="],
451
 
452
- "@typescript-eslint/parser": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.24.0", "@typescript-eslint/types": "8.24.0", "@typescript-eslint/typescript-estree": "8.24.0", "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-MFDaO9CYiard9j9VepMNa9MTcqVvSny2N4hkY6roquzj8pdCBRENhErrteaQuu7Yjn1ppk0v1/ZF9CG3KIlrTA=="],
453
 
454
- "@typescript-eslint/scope-manager": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@typescript-eslint/types": "8.24.0", "@typescript-eslint/visitor-keys": "8.24.0" } }, "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw=="],
455
 
456
- "@typescript-eslint/type-utils": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.24.0", "@typescript-eslint/utils": "8.24.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-8fitJudrnY8aq0F1wMiPM1UUgiXQRJ5i8tFjq9kGfRajU+dbPyOuHbl0qRopLEidy0MwqgTHDt6CnSeXanNIwA=="],
457
 
458
- "@typescript-eslint/types": ["@typescript-eslint/[email protected].0", "", {}, "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw=="],
459
 
460
- "@typescript-eslint/typescript-estree": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@typescript-eslint/types": "8.24.0", "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ=="],
461
 
462
- "@typescript-eslint/utils": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.24.0", "@typescript-eslint/types": "8.24.0", "@typescript-eslint/typescript-estree": "8.24.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ=="],
463
 
464
- "@typescript-eslint/visitor-keys": ["@typescript-eslint/[email protected].0", "", { "dependencies": { "@typescript-eslint/types": "8.24.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg=="],
465
 
466
  "@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
467
 
@@ -515,7 +519,7 @@
515
 
516
  "braces": ["[email protected]", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
517
 
518
- "bun-types": ["[email protected].2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
519
 
520
  "call-bind": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
521
 
@@ -529,13 +533,13 @@
529
 
530
  "chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
531
 
532
- "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
533
 
534
  "character-entities-html4": ["[email protected]", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
535
 
536
- "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
537
 
538
- "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
539
 
540
  "class-variance-authority": ["[email protected]", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
541
 
@@ -619,7 +623,7 @@
619
 
620
  "escape-string-regexp": ["[email protected]", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
621
 
622
- "eslint": ["eslint@9.20.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.11.0", "@eslint/eslintrc": "^3.2.0", "@eslint/js": "9.20.0", "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g=="],
623
 
624
  "eslint-config-prettier": ["[email protected]", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
625
 
@@ -659,6 +663,8 @@
659
 
660
  "fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
661
 
 
 
662
  "file-entry-cache": ["[email protected]", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
663
 
664
  "file-selector": ["[email protected]", "", { "dependencies": { "tslib": "^2.7.0" } }, "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig=="],
@@ -679,6 +685,8 @@
679
 
680
  "form-data": ["[email protected]", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
681
 
 
 
682
  "fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
683
 
684
  "function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
@@ -743,10 +751,18 @@
743
 
744
  "hasown": ["[email protected]", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
745
 
 
 
746
  "hast-util-to-jsx-runtime": ["[email protected]", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", "style-to-object": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg=="],
747
 
748
  "hast-util-whitespace": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
749
 
 
 
 
 
 
 
750
  "hoist-non-react-statics": ["[email protected]", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
751
 
752
  "html-url-attributes": ["[email protected]", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
@@ -761,9 +777,9 @@
761
 
762
  "internal-slot": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
763
 
764
- "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
765
 
766
- "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
767
 
768
  "is-array-buffer": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
769
 
@@ -783,7 +799,7 @@
783
 
784
  "is-date-object": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
785
 
786
- "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
787
 
788
  "is-extglob": ["[email protected]", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
789
 
@@ -793,7 +809,7 @@
793
 
794
  "is-glob": ["[email protected]", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
795
 
796
- "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
797
 
798
  "is-map": ["[email protected]", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
799
 
@@ -883,6 +899,8 @@
883
 
884
  "loose-envify": ["[email protected]", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
885
 
 
 
886
  "lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg=="],
887
 
888
  "markdown-table": ["[email protected]", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
@@ -991,7 +1009,7 @@
991
 
992
  "minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
993
 
994
- "minisearch": ["[email protected].1", "", {}, "sha512-b3YZEYCEH4EdCAtYP7OlDyx7FdPwNzuNwLQ34SfJpM9dlbBZzeXndGavTrC+VCiRWomL21SWfMc6SCKO/U2ZNw=="],
995
 
996
  "mnemonist": ["[email protected]", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],
997
 
@@ -1029,7 +1047,7 @@
1029
 
1030
  "parent-module": ["[email protected]", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
1031
 
1032
- "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
1033
 
1034
  "parse-json": ["[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
1035
 
@@ -1047,16 +1065,18 @@
1047
 
1048
  "possible-typed-array-names": ["[email protected]", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
1049
 
1050
- "postcss": ["[email protected].1", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ=="],
1051
 
1052
  "prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
1053
 
1054
- "prettier": ["[email protected].1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw=="],
1055
 
1056
  "prettier-plugin-tailwindcss": ["[email protected]", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="],
1057
 
1058
  "prism-react-renderer": ["[email protected]", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
1059
 
 
 
1060
  "prop-types": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
1061
 
1062
  "property-information": ["[email protected]", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
@@ -1071,11 +1091,11 @@
1071
 
1072
  "react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
1073
 
1074
- "react-dropzone": ["[email protected].5", "", { "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">= 16.8 || 18.0.0" } }, "sha512-9nDUaEEpqZLOz5v5SUcFA0CjM4vq8YbqO0WRls+EYT7+DvxUdzDPKNCPLqGfj3YL9MsniCLCD4RFA6M95V6KMQ=="],
1075
 
1076
  "react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
1077
 
1078
- "react-markdown": ["[email protected].3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw=="],
1079
 
1080
  "react-number-format": ["[email protected]", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ=="],
1081
 
@@ -1087,10 +1107,14 @@
1087
 
1088
  "react-style-singleton": ["[email protected]", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
1089
 
 
 
1090
  "react-transition-group": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
1091
 
1092
  "reflect.getprototypeof": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
1093
 
 
 
1094
  "regenerator-runtime": ["[email protected]", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
1095
 
1096
  "regexp.prototype.flags": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
@@ -1179,11 +1203,11 @@
1179
 
1180
  "supports-preserve-symlinks-flag": ["[email protected]", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
1181
 
1182
- "tailwind-merge": ["[email protected].1", "", {}, "sha512-AvzE8FmSoXC7nC+oU5GlQJbip2UO7tmOhOfQyOmPhrStOGXHU08j8mZEHZ4BmCqY5dWTCo4ClWkNyRNx1wpT0g=="],
1183
 
1184
- "tailwind-scrollbar": ["[email protected].0", "", { "dependencies": { "prism-react-renderer": "^2.4.1" }, "peerDependencies": { "tailwindcss": "4.x" } }, "sha512-elqx9m09VHY8gkrMiyimFO09JlS3AyLFXT0eaLaWPi7ImwHlbZj1ce/AxSis2LtR+ewBGEyUV7URNEMcjP1Z2w=="],
1185
 
1186
- "tailwindcss": ["[email protected].6", "", {}, "sha512-mysewHYJKaXgNOW6pp5xon/emCsfAMnO8WMaGKZZ35fomnR/T5gYnRg2/yRTTrtXiEl1tiVkeRt0eMO6HxEZqw=="],
1187
 
1188
  "tailwindcss-animate": ["[email protected]", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
1189
 
@@ -1211,7 +1235,7 @@
1211
 
1212
  "typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
1213
 
1214
- "typescript-eslint": ["[email protected].0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.0", "@typescript-eslint/parser": "8.24.0", "@typescript-eslint/utils": "8.24.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-/lmv4366en/qbB32Vz5+kCNZEMf6xYHwh1z48suBwZvAtnXKbP+YhGe8OLE2BqC67LMqKkCNLtjejdwsdW6uOQ=="],
1215
 
1216
  "unbox-primitive": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
1217
 
@@ -1245,7 +1269,7 @@
1245
 
1246
  "vfile-message": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
1247
 
1248
- "vite": ["[email protected].0", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.5.1", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-RjjMipCKVoR4hVfPY6GQTgveinjNuyLw+qruksLDvA5ktI1150VmcMBKmQaEWJhg/j6Uaf6dNCNA0AfdzUb/hQ=="],
1249
 
1250
  "which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
1251
 
@@ -1259,6 +1283,8 @@
1259
 
1260
  "word-wrap": ["[email protected]", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
1261
 
 
 
1262
  "yaml": ["[email protected]", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
1263
 
1264
  "yocto-queue": ["[email protected]", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
@@ -1273,8 +1299,6 @@
1273
 
1274
  "@eslint/eslintrc/globals": ["[email protected]", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
1275
 
1276
- "@eslint/plugin-kit/@eslint/core": ["@eslint/[email protected]", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw=="],
1277
-
1278
  "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
1279
 
1280
  "@types/ws/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
@@ -1285,14 +1309,42 @@
1285
 
1286
  "babel-plugin-macros/resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
1287
 
1288
- "bun-types/@types/node": ["@types/node@22.13.1", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
1289
 
1290
  "fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
1291
 
 
 
 
 
 
 
 
 
1292
  "mdast-util-find-and-replace/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
1293
 
1294
- "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
 
 
 
 
1295
 
1296
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1297
  }
1298
  }
 
35
  "graphology": "^0.26.0",
36
  "graphology-generators": "^0.11.2",
37
  "lucide-react": "^0.475.0",
38
+ "minisearch": "^7.1.2",
39
  "react": "^19.0.0",
40
  "react-dom": "^19.0.0",
41
+ "react-dropzone": "^14.3.6",
42
+ "react-markdown": "^9.1.0",
43
  "react-number-format": "^5.4.3",
44
+ "react-syntax-highlighter": "^15.6.1",
45
  "rehype-react": "^8.0.0",
46
  "remark-gfm": "^4.0.1",
47
  "remark-math": "^6.0.0",
48
  "seedrandom": "^3.0.5",
49
  "sigma": "^3.0.1",
50
  "sonner": "^1.7.4",
51
+ "tailwind-merge": "^3.0.2",
52
+ "tailwind-scrollbar": "^4.0.1",
53
  "zustand": "^5.0.3",
54
  },
55
  "devDependencies": {
56
+ "@eslint/js": "^9.21.0",
57
  "@stylistic/eslint-plugin-js": "^3.1.0",
58
+ "@tailwindcss/vite": "^4.0.8",
59
+ "@types/bun": "^1.2.3",
60
+ "@types/node": "^22.13.5",
61
  "@types/react": "^19.0.10",
62
  "@types/react-dom": "^19.0.4",
63
+ "@types/react-syntax-highlighter": "^15.5.13",
64
  "@types/seedrandom": "^3.0.8",
65
  "@vitejs/plugin-react-swc": "^3.8.0",
66
+ "eslint": "^9.21.0",
67
  "eslint-config-prettier": "^10.0.1",
68
  "eslint-plugin-react": "^7.37.4",
69
  "eslint-plugin-react-hooks": "^5.1.0",
70
  "eslint-plugin-react-refresh": "^0.4.19",
71
  "globals": "^15.15.0",
72
  "graphology-types": "^0.24.8",
73
+ "prettier": "^3.5.2",
74
  "prettier-plugin-tailwindcss": "^0.6.11",
75
+ "tailwindcss": "^4.0.8",
76
  "tailwindcss-animate": "^1.0.7",
77
  "typescript": "~5.7.3",
78
+ "typescript-eslint": "^8.24.1",
79
+ "vite": "^6.1.1",
80
  },
81
  },
82
  },
 
179
 
180
  "@eslint/config-array": ["@eslint/[email protected]", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w=="],
181
 
182
+ "@eslint/core": ["@eslint/core@0.12.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg=="],
183
 
184
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.0", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ=="],
185
 
186
+ "@eslint/js": ["@eslint/js@9.21.0", "", {}, "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw=="],
187
 
188
  "@eslint/object-schema": ["@eslint/[email protected]", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
189
 
190
+ "@eslint/plugin-kit": ["@eslint/[email protected].7", "", { "dependencies": { "@eslint/core": "^0.12.0", "levn": "^0.4.1" } }, "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g=="],
191
 
192
  "@faker-js/faker": ["@faker-js/[email protected]", "", {}, "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw=="],
193
 
 
205
 
206
  "@humanwhocodes/module-importer": ["@humanwhocodes/[email protected]", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
207
 
208
+ "@humanwhocodes/retry": ["@humanwhocodes/[email protected].2", "", {}, "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ=="],
209
 
210
  "@jridgewell/gen-mapping": ["@jridgewell/[email protected]", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
211
 
 
385
 
386
  "@swc/types": ["@swc/[email protected]", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ=="],
387
 
388
+ "@tailwindcss/node": ["@tailwindcss/[email protected].8", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "tailwindcss": "4.0.8" } }, "sha512-FKArQpbrbwv08TNT0k7ejYXpF+R8knZFAatNc0acOxbgeqLzwb86r+P3LGOjIeI3Idqe9CVkZrh4GlsJLJKkkw=="],
389
 
390
+ "@tailwindcss/oxide": ["@tailwindcss/[email protected].8", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.0.8", "@tailwindcss/oxide-darwin-arm64": "4.0.8", "@tailwindcss/oxide-darwin-x64": "4.0.8", "@tailwindcss/oxide-freebsd-x64": "4.0.8", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.0.8", "@tailwindcss/oxide-linux-arm64-gnu": "4.0.8", "@tailwindcss/oxide-linux-arm64-musl": "4.0.8", "@tailwindcss/oxide-linux-x64-gnu": "4.0.8", "@tailwindcss/oxide-linux-x64-musl": "4.0.8", "@tailwindcss/oxide-win32-arm64-msvc": "4.0.8", "@tailwindcss/oxide-win32-x64-msvc": "4.0.8" } }, "sha512-KfMcuAu/Iw+DcV1e8twrFyr2yN8/ZDC/odIGta4wuuJOGkrkHZbvJvRNIbQNhGh7erZTYV6Ie0IeD6WC9Y8Hcw=="],
391
 
392
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/[email protected].8", "", { "os": "android", "cpu": "arm64" }, "sha512-We7K79+Sm4mwJHk26Yzu/GAj7C7myemm7PeXvpgMxyxO70SSFSL3uCcqFbz9JA5M5UPkrl7N9fkBe/Y0iazqpA=="],
393
 
394
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/[email protected].8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Lv9Isi2EwkCTG1sRHNDi0uRNN1UGFdEThUAGFrydRmQZnraGLMjN8gahzg2FFnOizDl7LB2TykLUuiw833DSNg=="],
395
 
396
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/[email protected].8", "", { "os": "darwin", "cpu": "x64" }, "sha512-fWfywfYIlSWtKoqWTjukTHLWV3ARaBRjXCC2Eo0l6KVpaqGY4c2y8snUjp1xpxUtpqwMvCvFWFaleMoz1Vhzlw=="],
397
 
398
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/[email protected].8", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SO+dyvjJV9G94bnmq2288Ke0BIdvrbSbvtPLaQdqjqHR83v5L2fWADyFO+1oecHo9Owsk8MxcXh1agGVPIKIqw=="],
399
 
400
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/[email protected].8", "", { "os": "linux", "cpu": "arm" }, "sha512-ZSHggWiEblQNV69V0qUK5vuAtHP+I+S2eGrKGJ5lPgwgJeAd6GjLsVBN+Mqn2SPVfYM3BOpS9jX/zVg9RWQVDQ=="],
401
 
402
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/[email protected].8", "", { "os": "linux", "cpu": "arm64" }, "sha512-xWpr6M0OZLDNsr7+bQz+3X7zcnDJZJ1N9gtBWCtfhkEtDjjxYEp+Lr5L5nc/yXlL4MyCHnn0uonGVXy3fhxaVA=="],
403
 
404
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/[email protected].8", "", { "os": "linux", "cpu": "arm64" }, "sha512-5tz2IL7LN58ssGEq7h/staD7pu/izF/KeMWdlJ86WDe2Ah46LF3ET6ZGKTr5eZMrnEA0M9cVFuSPprKRHNgjeg=="],
405
 
406
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/[email protected].8", "", { "os": "linux", "cpu": "x64" }, "sha512-KSzMkhyrxAQyY2o194NKVKU9j/c+NFSoMvnHWFaNHKi3P1lb+Vq1UC19tLHrmxSkKapcMMu69D7+G1+FVGNDXQ=="],
407
 
408
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/[email protected].8", "", { "os": "linux", "cpu": "x64" }, "sha512-yFYKG5UtHTRimjtqxUWXBgI4Tc6NJe3USjRIVdlTczpLRxq/SFwgzGl5JbatCxgSRDPBFwRrNPxq+ukfQFGdrw=="],
409
 
410
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/[email protected].8", "", { "os": "win32", "cpu": "arm64" }, "sha512-tndGujmCSba85cRCnQzXgpA2jx5gXimyspsUYae5jlPyLRG0RjXbDshFKOheVXU4TLflo7FSG8EHCBJ0EHTKdQ=="],
411
 
412
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/[email protected].8", "", { "os": "win32", "cpu": "x64" }, "sha512-T77jroAc0p4EHVVgTUiNeFn6Nj3jtD3IeNId2X+0k+N1XxfNipy81BEkYErpKLiOkNhpNFjPee8/ZVas29b2OQ=="],
413
 
414
+ "@tailwindcss/vite": ["@tailwindcss/[email protected].8", "", { "dependencies": { "@tailwindcss/node": "4.0.8", "@tailwindcss/oxide": "4.0.8", "lightningcss": "^1.29.1", "tailwindcss": "4.0.8" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-+SAq44yLzYlzyrb7QTcFCdU8Xa7FOA0jp+Xby7fPMUie+MY9HhJysM7Vp+vL8qIp8ceQJfLD+FjgJuJ4lL6nyg=="],
415
 
416
+ "@types/bun": ["@types/[email protected].3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="],
417
 
418
  "@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
419
 
 
431
 
432
  "@types/ms": ["@types/[email protected]", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
433
 
434
+ "@types/node": ["@types/[email protected].5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="],
435
 
436
  "@types/parse-json": ["@types/[email protected]", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
437
 
 
441
 
442
  "@types/react-dom": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg=="],
443
 
444
+ "@types/react-syntax-highlighter": ["@types/[email protected]", "", { "dependencies": { "@types/react": "*" } }, "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA=="],
445
+
446
  "@types/react-transition-group": ["@types/[email protected]", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
447
 
448
  "@types/seedrandom": ["@types/[email protected]", "", {}, "sha512-TY1eezMU2zH2ozQoAFAQFOPpvP15g+ZgSfTZt31AUUH/Rxtnz3H+A/Sv1Snw2/amp//omibc+AEkTaA8KUeOLQ=="],
 
451
 
452
  "@types/ws": ["@types/[email protected]", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
453
 
454
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/type-utils": "8.24.1", "@typescript-eslint/utils": "8.24.1", "@typescript-eslint/visitor-keys": "8.24.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-ll1StnKtBigWIGqvYDVuDmXJHVH4zLVot1yQ4fJtLpL7qacwkxJc1T0bptqw+miBQ/QfUbhl1TcQ4accW5KUyA=="],
455
 
456
+ "@typescript-eslint/parser": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/types": "8.24.1", "@typescript-eslint/typescript-estree": "8.24.1", "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-Tqoa05bu+t5s8CTZFaGpCH2ub3QeT9YDkXbPd3uQ4SfsLoh1/vv2GEYAioPoxCWJJNsenXlC88tRjwoHNts1oQ=="],
457
 
458
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@typescript-eslint/types": "8.24.1", "@typescript-eslint/visitor-keys": "8.24.1" } }, "sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q=="],
459
 
460
+ "@typescript-eslint/type-utils": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.24.1", "@typescript-eslint/utils": "8.24.1", "debug": "^4.3.4", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-/Do9fmNgCsQ+K4rCz0STI7lYB4phTtEXqqCAs3gZW0pnK7lWNkvWd5iW545GSmApm4AzmQXmSqXPO565B4WVrw=="],
461
 
462
+ "@typescript-eslint/types": ["@typescript-eslint/[email protected].1", "", {}, "sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A=="],
463
 
464
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@typescript-eslint/types": "8.24.1", "@typescript-eslint/visitor-keys": "8.24.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.0.1" }, "peerDependencies": { "typescript": ">=4.8.4 <5.8.0" } }, "sha512-UPyy4MJ/0RE648DSKQe9g0VDSehPINiejjA6ElqnFaFIhI6ZEiZAkUI0D5MCk0bQcTf/LVqZStvQ6K4lPn/BRg=="],
465
 
466
+ "@typescript-eslint/utils": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@typescript-eslint/scope-manager": "8.24.1", "@typescript-eslint/types": "8.24.1", "@typescript-eslint/typescript-estree": "8.24.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-OOcg3PMMQx9EXspId5iktsI3eMaXVwlhC8BvNnX6B5w9a4dVgpkQZuU8Hy67TolKcl+iFWq0XX+jbDGN4xWxjQ=="],
467
 
468
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/[email protected].1", "", { "dependencies": { "@typescript-eslint/types": "8.24.1", "eslint-visitor-keys": "^4.2.0" } }, "sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg=="],
469
 
470
  "@ungap/structured-clone": ["@ungap/[email protected]", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
471
 
 
519
 
520
  "braces": ["[email protected]", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
521
 
522
+ "bun-types": ["[email protected].3", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P7AeyTseLKAvgaZqQrvp3RqFM3yN9PlcLuSTe7SoJOfZkER73mLdT2vEQi8U64S1YvM/ldcNiQjn0Sn7H9lGgg=="],
523
 
524
  "call-bind": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
525
 
 
533
 
534
  "chalk": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
535
 
536
+ "character-entities": ["character-entities@1.2.4", "", {}, "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="],
537
 
538
  "character-entities-html4": ["[email protected]", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
539
 
540
+ "character-entities-legacy": ["character-entities-legacy@1.1.4", "", {}, "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="],
541
 
542
+ "character-reference-invalid": ["character-reference-invalid@1.1.4", "", {}, "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="],
543
 
544
  "class-variance-authority": ["[email protected]", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
545
 
 
623
 
624
  "escape-string-regexp": ["[email protected]", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
625
 
626
+ "eslint": ["eslint@9.21.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.2", "@eslint/core": "^0.12.0", "@eslint/eslintrc": "^3.3.0", "@eslint/js": "9.21.0", "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg=="],
627
 
628
  "eslint-config-prettier": ["[email protected]", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "build/bin/cli.js" } }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="],
629
 
 
663
 
664
  "fastq": ["[email protected]", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
665
 
666
+ "fault": ["[email protected]", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="],
667
+
668
  "file-entry-cache": ["[email protected]", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
669
 
670
  "file-selector": ["[email protected]", "", { "dependencies": { "tslib": "^2.7.0" } }, "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig=="],
 
685
 
686
  "form-data": ["[email protected]", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw=="],
687
 
688
+ "format": ["[email protected]", "", {}, "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww=="],
689
+
690
  "fsevents": ["[email protected]", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
691
 
692
  "function-bind": ["[email protected]", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
 
751
 
752
  "hasown": ["[email protected]", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
753
 
754
+ "hast-util-parse-selector": ["[email protected]", "", {}, "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ=="],
755
+
756
  "hast-util-to-jsx-runtime": ["[email protected]", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", "style-to-object": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg=="],
757
 
758
  "hast-util-whitespace": ["[email protected]", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
759
 
760
+ "hastscript": ["[email protected]", "", { "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^1.0.0", "hast-util-parse-selector": "^2.0.0", "property-information": "^5.0.0", "space-separated-tokens": "^1.0.0" } }, "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w=="],
761
+
762
+ "highlight.js": ["[email protected]", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="],
763
+
764
+ "highlightjs-vue": ["[email protected]", "", {}, "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA=="],
765
+
766
  "hoist-non-react-statics": ["[email protected]", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
767
 
768
  "html-url-attributes": ["[email protected]", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
 
777
 
778
  "internal-slot": ["[email protected]", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
779
 
780
+ "is-alphabetical": ["is-alphabetical@1.0.4", "", {}, "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="],
781
 
782
+ "is-alphanumerical": ["is-alphanumerical@1.0.4", "", { "dependencies": { "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0" } }, "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A=="],
783
 
784
  "is-array-buffer": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
785
 
 
799
 
800
  "is-date-object": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
801
 
802
+ "is-decimal": ["is-decimal@1.0.4", "", {}, "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="],
803
 
804
  "is-extglob": ["[email protected]", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
805
 
 
809
 
810
  "is-glob": ["[email protected]", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
811
 
812
+ "is-hexadecimal": ["is-hexadecimal@1.0.4", "", {}, "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="],
813
 
814
  "is-map": ["[email protected]", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
815
 
 
899
 
900
  "loose-envify": ["[email protected]", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
901
 
902
+ "lowlight": ["[email protected]", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="],
903
+
904
  "lucide-react": ["[email protected]", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg=="],
905
 
906
  "markdown-table": ["[email protected]", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
 
1009
 
1010
  "minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
1011
 
1012
+ "minisearch": ["[email protected].2", "", {}, "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA=="],
1013
 
1014
  "mnemonist": ["[email protected]", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],
1015
 
 
1047
 
1048
  "parent-module": ["[email protected]", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
1049
 
1050
+ "parse-entities": ["parse-entities@2.0.0", "", { "dependencies": { "character-entities": "^1.0.0", "character-entities-legacy": "^1.0.0", "character-reference-invalid": "^1.0.0", "is-alphanumerical": "^1.0.0", "is-decimal": "^1.0.0", "is-hexadecimal": "^1.0.0" } }, "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ=="],
1051
 
1052
  "parse-json": ["[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
1053
 
 
1065
 
1066
  "possible-typed-array-names": ["[email protected]", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
1067
 
1068
+ "postcss": ["[email protected].3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
1069
 
1070
  "prelude-ls": ["[email protected]", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
1071
 
1072
+ "prettier": ["[email protected].2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg=="],
1073
 
1074
  "prettier-plugin-tailwindcss": ["[email protected]", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA=="],
1075
 
1076
  "prism-react-renderer": ["[email protected]", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
1077
 
1078
+ "prismjs": ["[email protected]", "", {}, "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="],
1079
+
1080
  "prop-types": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
1081
 
1082
  "property-information": ["[email protected]", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="],
 
1091
 
1092
  "react-dom": ["[email protected]", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
1093
 
1094
+ "react-dropzone": ["[email protected].6", "", { "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">= 16.8 || 18.0.0" } }, "sha512-U792j+x0rcwH/U/Slv/OBNU/LGFYbDLHKKiJoPhNaOianayZevCt4Y5S0CraPssH/6/wT6xhKDfzdXUgCBS0HQ=="],
1095
 
1096
  "react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
1097
 
1098
+ "react-markdown": ["react-markdown@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw=="],
1099
 
1100
  "react-number-format": ["[email protected]", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ=="],
1101
 
 
1107
 
1108
  "react-style-singleton": ["[email protected]", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
1109
 
1110
+ "react-syntax-highlighter": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.3.1", "highlight.js": "^10.4.1", "highlightjs-vue": "^1.0.0", "lowlight": "^1.17.0", "prismjs": "^1.27.0", "refractor": "^3.6.0" }, "peerDependencies": { "react": ">= 0.14.0" } }, "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg=="],
1111
+
1112
  "react-transition-group": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
1113
 
1114
  "reflect.getprototypeof": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
1115
 
1116
+ "refractor": ["[email protected]", "", { "dependencies": { "hastscript": "^6.0.0", "parse-entities": "^2.0.0", "prismjs": "~1.27.0" } }, "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA=="],
1117
+
1118
  "regenerator-runtime": ["[email protected]", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="],
1119
 
1120
  "regexp.prototype.flags": ["[email protected]", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
 
1203
 
1204
  "supports-preserve-symlinks-flag": ["[email protected]", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
1205
 
1206
+ "tailwind-merge": ["[email protected].2", "", {}, "sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw=="],
1207
 
1208
+ "tailwind-scrollbar": ["[email protected].1", "", { "dependencies": { "prism-react-renderer": "^2.4.1" }, "peerDependencies": { "tailwindcss": "4.x" } }, "sha512-j2ZfUI7p8xmSQdlqaCxEb4Mha8ErvWjDVyu2Ke4IstWprQ/6TmIz1GSLE62vsTlXwnMLYhuvbFbIFzaJGOGtMg=="],
1209
 
1210
+ "tailwindcss": ["[email protected].8", "", {}, "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw=="],
1211
 
1212
  "tailwindcss-animate": ["[email protected]", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
1213
 
 
1235
 
1236
  "typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
1237
 
1238
+ "typescript-eslint": ["[email protected].1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.24.1", "@typescript-eslint/parser": "8.24.1", "@typescript-eslint/utils": "8.24.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.8.0" } }, "sha512-cw3rEdzDqBs70TIcb0Gdzbt6h11BSs2pS0yaq7hDWDBtCCSei1pPSUXE9qUdQ/Wm9NgFg8mKtMt1b8fTHIl1jA=="],
1239
 
1240
  "unbox-primitive": ["[email protected]", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
1241
 
 
1269
 
1270
  "vfile-message": ["[email protected]", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="],
1271
 
1272
+ "vite": ["[email protected].1", "", { "dependencies": { "esbuild": "^0.24.2", "postcss": "^8.5.2", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA=="],
1273
 
1274
  "which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
1275
 
 
1283
 
1284
  "word-wrap": ["[email protected]", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
1285
 
1286
+ "xtend": ["[email protected]", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
1287
+
1288
  "yaml": ["[email protected]", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="],
1289
 
1290
  "yocto-queue": ["[email protected]", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
 
1299
 
1300
  "@eslint/eslintrc/globals": ["[email protected]", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
1301
 
 
 
1302
  "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/[email protected]", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
1303
 
1304
  "@types/ws/@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew=="],
 
1309
 
1310
  "babel-plugin-macros/resolve": ["[email protected]", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
1311
 
1312
+ "decode-named-character-reference/character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
1313
 
1314
  "fast-glob/glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
1315
 
1316
+ "hastscript/@types/hast": ["@types/[email protected]", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
1317
+
1318
+ "hastscript/comma-separated-tokens": ["[email protected]", "", {}, "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw=="],
1319
+
1320
+ "hastscript/property-information": ["[email protected]", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA=="],
1321
+
1322
+ "hastscript/space-separated-tokens": ["[email protected]", "", {}, "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="],
1323
+
1324
  "mdast-util-find-and-replace/escape-string-regexp": ["[email protected]", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
1325
 
1326
+ "mdast-util-mdx-jsx/parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
1327
+
1328
+ "refractor/prismjs": ["[email protected]", "", {}, "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA=="],
1329
+
1330
+ "stringify-entities/character-entities-legacy": ["[email protected]", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
1331
 
1332
  "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["[email protected]", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
1333
+
1334
+ "hastscript/@types/hast/@types/unist": ["@types/[email protected]", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
1335
+
1336
+ "mdast-util-mdx-jsx/parse-entities/@types/unist": ["@types/[email protected]", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
1337
+
1338
+ "mdast-util-mdx-jsx/parse-entities/character-entities-legacy": ["[email protected]", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
1339
+
1340
+ "mdast-util-mdx-jsx/parse-entities/character-reference-invalid": ["[email protected]", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="],
1341
+
1342
+ "mdast-util-mdx-jsx/parse-entities/is-alphanumerical": ["[email protected]", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
1343
+
1344
+ "mdast-util-mdx-jsx/parse-entities/is-decimal": ["[email protected]", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
1345
+
1346
+ "mdast-util-mdx-jsx/parse-entities/is-hexadecimal": ["[email protected]", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
1347
+
1348
+ "mdast-util-mdx-jsx/parse-entities/is-alphanumerical/is-alphabetical": ["[email protected]", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
1349
  }
1350
  }
lightrag_webui/index.html CHANGED
@@ -2,7 +2,7 @@
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <title>Lightrag</title>
8
  </head>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/logo.png" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
  <title>Lightrag</title>
8
  </head>
lightrag_webui/package.json CHANGED
@@ -44,45 +44,47 @@
44
  "graphology": "^0.26.0",
45
  "graphology-generators": "^0.11.2",
46
  "lucide-react": "^0.475.0",
47
- "minisearch": "^7.1.1",
48
  "react": "^19.0.0",
49
  "react-dom": "^19.0.0",
50
- "react-dropzone": "^14.3.5",
51
- "react-markdown": "^9.0.3",
52
  "react-number-format": "^5.4.3",
 
53
  "rehype-react": "^8.0.0",
54
  "remark-gfm": "^4.0.1",
55
  "remark-math": "^6.0.0",
56
  "seedrandom": "^3.0.5",
57
  "sigma": "^3.0.1",
58
  "sonner": "^1.7.4",
59
- "tailwind-merge": "^3.0.1",
60
- "tailwind-scrollbar": "^4.0.0",
61
  "zustand": "^5.0.3"
62
  },
63
  "devDependencies": {
64
- "@eslint/js": "^9.20.0",
65
  "@stylistic/eslint-plugin-js": "^3.1.0",
66
- "@tailwindcss/vite": "^4.0.6",
67
- "@types/bun": "^1.2.2",
68
- "@types/node": "^22.13.4",
69
  "@types/react": "^19.0.10",
70
  "@types/react-dom": "^19.0.4",
 
71
  "@types/seedrandom": "^3.0.8",
72
  "@vitejs/plugin-react-swc": "^3.8.0",
73
- "eslint": "^9.20.1",
74
  "eslint-config-prettier": "^10.0.1",
75
  "eslint-plugin-react": "^7.37.4",
76
  "eslint-plugin-react-hooks": "^5.1.0",
77
  "eslint-plugin-react-refresh": "^0.4.19",
78
  "globals": "^15.15.0",
79
  "graphology-types": "^0.24.8",
80
- "prettier": "^3.5.1",
81
  "prettier-plugin-tailwindcss": "^0.6.11",
82
- "tailwindcss": "^4.0.6",
83
  "tailwindcss-animate": "^1.0.7",
84
  "typescript": "~5.7.3",
85
- "typescript-eslint": "^8.24.0",
86
- "vite": "^6.1.0"
87
  }
88
  }
 
44
  "graphology": "^0.26.0",
45
  "graphology-generators": "^0.11.2",
46
  "lucide-react": "^0.475.0",
47
+ "minisearch": "^7.1.2",
48
  "react": "^19.0.0",
49
  "react-dom": "^19.0.0",
50
+ "react-dropzone": "^14.3.6",
51
+ "react-markdown": "^9.1.0",
52
  "react-number-format": "^5.4.3",
53
+ "react-syntax-highlighter": "^15.6.1",
54
  "rehype-react": "^8.0.0",
55
  "remark-gfm": "^4.0.1",
56
  "remark-math": "^6.0.0",
57
  "seedrandom": "^3.0.5",
58
  "sigma": "^3.0.1",
59
  "sonner": "^1.7.4",
60
+ "tailwind-merge": "^3.0.2",
61
+ "tailwind-scrollbar": "^4.0.1",
62
  "zustand": "^5.0.3"
63
  },
64
  "devDependencies": {
65
+ "@eslint/js": "^9.21.0",
66
  "@stylistic/eslint-plugin-js": "^3.1.0",
67
+ "@tailwindcss/vite": "^4.0.8",
68
+ "@types/bun": "^1.2.3",
69
+ "@types/node": "^22.13.5",
70
  "@types/react": "^19.0.10",
71
  "@types/react-dom": "^19.0.4",
72
+ "@types/react-syntax-highlighter": "^15.5.13",
73
  "@types/seedrandom": "^3.0.8",
74
  "@vitejs/plugin-react-swc": "^3.8.0",
75
+ "eslint": "^9.21.0",
76
  "eslint-config-prettier": "^10.0.1",
77
  "eslint-plugin-react": "^7.37.4",
78
  "eslint-plugin-react-hooks": "^5.1.0",
79
  "eslint-plugin-react-refresh": "^0.4.19",
80
  "globals": "^15.15.0",
81
  "graphology-types": "^0.24.8",
82
+ "prettier": "^3.5.2",
83
  "prettier-plugin-tailwindcss": "^0.6.11",
84
+ "tailwindcss": "^4.0.8",
85
  "tailwindcss-animate": "^1.0.7",
86
  "typescript": "~5.7.3",
87
+ "typescript-eslint": "^8.24.1",
88
+ "vite": "^6.1.1"
89
  }
90
  }
lightrag_webui/public/logo.png ADDED

Git LFS Details

  • SHA256: 38f318052f4521251aafb98e9dde098630d4ac4ff3ef21045ef4e651a8e4926c
  • Pointer size: 131 Bytes
  • Size of remote file: 159 kB
lightrag_webui/public/vite.svg DELETED
lightrag_webui/src/api/lightrag.ts CHANGED
@@ -161,8 +161,8 @@ axiosInstance.interceptors.response.use(
161
  )
162
 
163
  // API methods
164
- export const queryGraphs = async (label: string): Promise<LightragGraphType> => {
165
- const response = await axiosInstance.get(`/graphs?label=${label}`)
166
  return response.data
167
  }
168
 
 
161
  )
162
 
163
  // API methods
164
+ export const queryGraphs = async (label: string, maxDepth: number): Promise<LightragGraphType> => {
165
+ const response = await axiosInstance.get(`/graphs?label=${label}&max_depth=${maxDepth}`)
166
  return response.data
167
  }
168
 
lightrag_webui/src/components/graph/GraphControl.tsx CHANGED
@@ -26,8 +26,10 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
26
  const registerEvents = useRegisterEvents<NodeType, EdgeType>()
27
  const setSettings = useSetSettings<NodeType, EdgeType>()
28
  const loadGraph = useLoadGraph<NodeType, EdgeType>()
 
 
29
  const { assign: assignLayout } = useLayoutForceAtlas2({
30
- iterations: 20
31
  })
32
 
33
  const { theme } = useTheme()
 
26
  const registerEvents = useRegisterEvents<NodeType, EdgeType>()
27
  const setSettings = useSetSettings<NodeType, EdgeType>()
28
  const loadGraph = useLoadGraph<NodeType, EdgeType>()
29
+
30
+ const maxIterations = useSettingsStore.use.graphLayoutMaxIterations()
31
  const { assign: assignLayout } = useLayoutForceAtlas2({
32
+ iterations: maxIterations
33
  })
34
 
35
  const { theme } = useTheme()
lightrag_webui/src/components/graph/GraphLabels.tsx CHANGED
@@ -1,67 +1,81 @@
1
- import { useCallback, useState } from 'react'
2
  import { AsyncSelect } from '@/components/ui/AsyncSelect'
3
  import { getGraphLabels } from '@/api/lightrag'
4
  import { useSettingsStore } from '@/stores/settings'
 
 
5
  import MiniSearch from 'minisearch'
6
 
 
 
 
 
 
 
7
  const GraphLabels = () => {
8
  const label = useSettingsStore.use.queryLabel()
9
- const [labels, setLabels] = useState<{
10
- labels: string[]
11
- searchEngine: MiniSearch | null
12
- }>({
13
- labels: [],
14
- searchEngine: null
15
- })
16
- const [fetched, setFetched] = useState(false)
17
 
18
- const fetchData = useCallback(
19
- async (query?: string): Promise<string[]> => {
20
- let _labels = labels.labels
21
- let _searchEngine = labels.searchEngine
 
 
 
 
22
 
23
- if (!fetched || !_searchEngine) {
24
- _labels = ['*'].concat(await getGraphLabels())
 
 
25
 
26
- // Ensure query label exists
27
- if (!_labels.includes(useSettingsStore.getState().queryLabel)) {
28
- useSettingsStore.getState().setQueryLabel(_labels[0])
 
 
 
 
 
 
29
  }
 
 
30
 
31
- // Create search engine
32
- _searchEngine = new MiniSearch({
33
- idField: 'id',
34
- fields: ['value'],
35
- searchOptions: {
36
- prefix: true,
37
- fuzzy: 0.2,
38
- boost: {
39
- label: 2
40
- }
41
- }
42
- })
43
 
44
- // Add documents
45
- const documents = _labels.map((str, index) => ({ id: index, value: str }))
46
- _searchEngine.addAll(documents)
47
 
48
- setLabels({
49
- labels: _labels,
50
- searchEngine: _searchEngine
51
- })
52
- setFetched(true)
53
- }
54
- if (!query) {
55
- return _labels
 
 
 
 
 
 
56
  }
57
 
58
- // Search labels
59
- return _searchEngine.search(query).map((result) => _labels[result.id])
 
60
  },
61
- [labels, fetched, setLabels, setFetched]
62
  )
63
 
64
  const setQueryLabel = useCallback((label: string) => {
 
65
  useSettingsStore.getState().setQueryLabel(label)
66
  }, [])
67
 
 
1
+ import { useCallback } from 'react'
2
  import { AsyncSelect } from '@/components/ui/AsyncSelect'
3
  import { getGraphLabels } from '@/api/lightrag'
4
  import { useSettingsStore } from '@/stores/settings'
5
+ import { useGraphStore } from '@/stores/graph'
6
+ import { labelListLimit } from '@/lib/constants'
7
  import MiniSearch from 'minisearch'
8
 
9
+ const lastGraph: any = {
10
+ graph: null,
11
+ searchEngine: null,
12
+ labels: []
13
+ }
14
+
15
  const GraphLabels = () => {
16
  const label = useSettingsStore.use.queryLabel()
17
+ const graph = useGraphStore.use.sigmaGraph()
 
 
 
 
 
 
 
18
 
19
+ const getSearchEngine = useCallback(async () => {
20
+ if (lastGraph.graph == graph) {
21
+ return {
22
+ labels: lastGraph.labels,
23
+ searchEngine: lastGraph.searchEngine
24
+ }
25
+ }
26
+ const labels = ['*'].concat(await getGraphLabels())
27
 
28
+ // Ensure query label exists
29
+ if (!labels.includes(useSettingsStore.getState().queryLabel)) {
30
+ useSettingsStore.getState().setQueryLabel(labels[0])
31
+ }
32
 
33
+ // Create search engine
34
+ const searchEngine = new MiniSearch({
35
+ idField: 'id',
36
+ fields: ['value'],
37
+ searchOptions: {
38
+ prefix: true,
39
+ fuzzy: 0.2,
40
+ boost: {
41
+ label: 2
42
  }
43
+ }
44
+ })
45
 
46
+ // Add documents
47
+ const documents = labels.map((str, index) => ({ id: index, value: str }))
48
+ searchEngine.addAll(documents)
 
 
 
 
 
 
 
 
 
49
 
50
+ lastGraph.graph = graph
51
+ lastGraph.searchEngine = searchEngine
52
+ lastGraph.labels = labels
53
 
54
+ return {
55
+ labels,
56
+ searchEngine
57
+ }
58
+ }, [graph])
59
+
60
+ const fetchData = useCallback(
61
+ async (query?: string): Promise<string[]> => {
62
+ const { labels, searchEngine } = await getSearchEngine()
63
+
64
+ let result: string[] = labels
65
+ if (query) {
66
+ // Search labels
67
+ result = searchEngine.search(query).map((r) => labels[r.id])
68
  }
69
 
70
+ return result.length <= labelListLimit
71
+ ? result
72
+ : [...result.slice(0, labelListLimit), `And ${result.length - labelListLimit} others`]
73
  },
74
+ [getSearchEngine]
75
  )
76
 
77
  const setQueryLabel = useCallback((label: string) => {
78
+ if (label.startsWith('And ') && label.endsWith(' others')) return
79
  useSettingsStore.getState().setQueryLabel(label)
80
  }, [])
81
 
lightrag_webui/src/components/graph/GraphSearch.tsx CHANGED
@@ -46,7 +46,7 @@ export const GraphSearchInput = ({
46
  }) => {
47
  const graph = useGraphStore.use.sigmaGraph()
48
 
49
- const search = useMemo(() => {
50
  if (lastGraph.graph == graph) {
51
  return lastGraph.searchEngine
52
  }
@@ -83,9 +83,9 @@ export const GraphSearchInput = ({
83
  const loadOptions = useCallback(
84
  async (query?: string): Promise<OptionItem[]> => {
85
  if (onFocus) onFocus(null)
86
- if (!query || !search) return []
87
- const result: OptionItem[] = search.search(query).map((result) => ({
88
- id: result.id,
89
  type: 'nodes'
90
  }))
91
 
@@ -101,7 +101,7 @@ export const GraphSearchInput = ({
101
  }
102
  ]
103
  },
104
- [search, onFocus]
105
  )
106
 
107
  return (
 
46
  }) => {
47
  const graph = useGraphStore.use.sigmaGraph()
48
 
49
+ const searchEngine = useMemo(() => {
50
  if (lastGraph.graph == graph) {
51
  return lastGraph.searchEngine
52
  }
 
83
  const loadOptions = useCallback(
84
  async (query?: string): Promise<OptionItem[]> => {
85
  if (onFocus) onFocus(null)
86
+ if (!query || !searchEngine) return []
87
+ const result: OptionItem[] = searchEngine.search(query).map((r) => ({
88
+ id: r.id,
89
  type: 'nodes'
90
  }))
91
 
 
101
  }
102
  ]
103
  },
104
+ [searchEngine, onFocus]
105
  )
106
 
107
  return (
lightrag_webui/src/components/graph/LayoutsControl.tsx CHANGED
@@ -13,6 +13,7 @@ import Button from '@/components/ui/Button'
13
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
14
  import { Command, CommandGroup, CommandItem, CommandList } from '@/components/ui/Command'
15
  import { controlButtonVariant } from '@/lib/constants'
 
16
 
17
  import { GripIcon, PlayIcon, PauseIcon } from 'lucide-react'
18
 
@@ -76,12 +77,14 @@ const LayoutsControl = () => {
76
  const [layout, setLayout] = useState<LayoutName>('Circular')
77
  const [opened, setOpened] = useState<boolean>(false)
78
 
 
 
79
  const layoutCircular = useLayoutCircular()
80
  const layoutCirclepack = useLayoutCirclepack()
81
  const layoutRandom = useLayoutRandom()
82
  const layoutNoverlap = useLayoutNoverlap({ settings: { margin: 1 } })
83
- const layoutForce = useLayoutForce({ maxIterations: 20 })
84
- const layoutForceAtlas2 = useLayoutForceAtlas2({ iterations: 20 })
85
  const workerNoverlap = useWorkerLayoutNoverlap()
86
  const workerForce = useWorkerLayoutForce()
87
  const workerForceAtlas2 = useWorkerLayoutForceAtlas2()
 
13
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
14
  import { Command, CommandGroup, CommandItem, CommandList } from '@/components/ui/Command'
15
  import { controlButtonVariant } from '@/lib/constants'
16
+ import { useSettingsStore } from '@/stores/settings'
17
 
18
  import { GripIcon, PlayIcon, PauseIcon } from 'lucide-react'
19
 
 
77
  const [layout, setLayout] = useState<LayoutName>('Circular')
78
  const [opened, setOpened] = useState<boolean>(false)
79
 
80
+ const maxIterations = useSettingsStore.use.graphLayoutMaxIterations()
81
+
82
  const layoutCircular = useLayoutCircular()
83
  const layoutCirclepack = useLayoutCirclepack()
84
  const layoutRandom = useLayoutRandom()
85
  const layoutNoverlap = useLayoutNoverlap({ settings: { margin: 1 } })
86
+ const layoutForce = useLayoutForce({ maxIterations: maxIterations })
87
+ const layoutForceAtlas2 = useLayoutForceAtlas2({ iterations: maxIterations })
88
  const workerNoverlap = useWorkerLayoutNoverlap()
89
  const workerForce = useWorkerLayoutForce()
90
  const workerForceAtlas2 = useWorkerLayoutForceAtlas2()
lightrag_webui/src/components/graph/Settings.tsx CHANGED
@@ -1,9 +1,10 @@
 
1
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
2
  import Checkbox from '@/components/ui/Checkbox'
3
  import Button from '@/components/ui/Button'
4
  import Separator from '@/components/ui/Separator'
5
  import Input from '@/components/ui/Input'
6
- import { useState, useCallback, useEffect } from 'react'
7
  import { controlButtonVariant } from '@/lib/constants'
8
  import { useSettingsStore } from '@/stores/settings'
9
  import { useBackendState } from '@/stores/state'
@@ -35,6 +36,74 @@ const LabeledCheckBox = ({
35
  )
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  /**
39
  * Component that displays a popover with settings options.
40
  */
@@ -45,11 +114,12 @@ export default function Settings() {
45
  const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
46
  const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
47
  const showNodeLabel = useSettingsStore.use.showNodeLabel()
48
-
49
  const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
50
  const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
51
  const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges()
52
  const showEdgeLabel = useSettingsStore.use.showEdgeLabel()
 
 
53
 
54
  const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
55
  const apiKey = useSettingsStore.use.apiKey()
@@ -102,6 +172,16 @@ export default function Settings() {
102
  []
103
  )
104
 
 
 
 
 
 
 
 
 
 
 
105
  const setApiKey = useCallback(async () => {
106
  useSettingsStore.setState({ apiKey: tempApiKey || null })
107
  await useBackendState.getState().check()
@@ -129,6 +209,14 @@ export default function Settings() {
129
  onCloseAutoFocus={(e) => e.preventDefault()}
130
  >
131
  <div className="flex flex-col gap-2">
 
 
 
 
 
 
 
 
132
  <LabeledCheckBox
133
  checked={showPropertyPanel}
134
  onCheckedChange={setShowPropertyPanel}
@@ -172,11 +260,18 @@ export default function Settings() {
172
  />
173
 
174
  <Separator />
175
-
176
- <LabeledCheckBox
177
- checked={enableHealthCheck}
178
- onCheckedChange={setEnableHealthCheck}
179
- label="Health Check"
 
 
 
 
 
 
 
180
  />
181
 
182
  <Separator />
 
1
+ import { useState, useCallback, useEffect } from 'react'
2
  import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
3
  import Checkbox from '@/components/ui/Checkbox'
4
  import Button from '@/components/ui/Button'
5
  import Separator from '@/components/ui/Separator'
6
  import Input from '@/components/ui/Input'
7
+
8
  import { controlButtonVariant } from '@/lib/constants'
9
  import { useSettingsStore } from '@/stores/settings'
10
  import { useBackendState } from '@/stores/state'
 
36
  )
37
  }
38
 
39
+ /**
40
+ * Component that displays a number input with a label.
41
+ */
42
+ const LabeledNumberInput = ({
43
+ value,
44
+ onEditFinished,
45
+ label,
46
+ min,
47
+ max
48
+ }: {
49
+ value: number
50
+ onEditFinished: (value: number) => void
51
+ label: string
52
+ min: number
53
+ max?: number
54
+ }) => {
55
+ const [currentValue, setCurrentValue] = useState<number | null>(value)
56
+
57
+ const onValueChange = useCallback(
58
+ (e: React.ChangeEvent<HTMLInputElement>) => {
59
+ const text = e.target.value.trim()
60
+ if (text.length === 0) {
61
+ setCurrentValue(null)
62
+ return
63
+ }
64
+ const newValue = Number.parseInt(text)
65
+ if (!isNaN(newValue) && newValue !== currentValue) {
66
+ if (min !== undefined && newValue < min) {
67
+ return
68
+ }
69
+ if (max !== undefined && newValue > max) {
70
+ return
71
+ }
72
+ setCurrentValue(newValue)
73
+ }
74
+ },
75
+ [currentValue, min, max]
76
+ )
77
+
78
+ const onBlur = useCallback(() => {
79
+ if (currentValue !== null && value !== currentValue) {
80
+ onEditFinished(currentValue)
81
+ }
82
+ }, [value, currentValue, onEditFinished])
83
+
84
+ return (
85
+ <div className="flex flex-col gap-2">
86
+ <label
87
+ htmlFor="terms"
88
+ className="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
89
+ >
90
+ {label}
91
+ </label>
92
+ <Input
93
+ value={currentValue || ''}
94
+ onChange={onValueChange}
95
+ className="h-6 w-full min-w-0"
96
+ onBlur={onBlur}
97
+ onKeyDown={(e) => {
98
+ if (e.key === 'Enter') {
99
+ onBlur()
100
+ }
101
+ }}
102
+ />
103
+ </div>
104
+ )
105
+ }
106
+
107
  /**
108
  * Component that displays a popover with settings options.
109
  */
 
114
  const showPropertyPanel = useSettingsStore.use.showPropertyPanel()
115
  const showNodeSearchBar = useSettingsStore.use.showNodeSearchBar()
116
  const showNodeLabel = useSettingsStore.use.showNodeLabel()
 
117
  const enableEdgeEvents = useSettingsStore.use.enableEdgeEvents()
118
  const enableNodeDrag = useSettingsStore.use.enableNodeDrag()
119
  const enableHideUnselectedEdges = useSettingsStore.use.enableHideUnselectedEdges()
120
  const showEdgeLabel = useSettingsStore.use.showEdgeLabel()
121
+ const graphQueryMaxDepth = useSettingsStore.use.graphQueryMaxDepth()
122
+ const graphLayoutMaxIterations = useSettingsStore.use.graphLayoutMaxIterations()
123
 
124
  const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
125
  const apiKey = useSettingsStore.use.apiKey()
 
172
  []
173
  )
174
 
175
+ const setGraphQueryMaxDepth = useCallback((depth: number) => {
176
+ if (depth < 1) return
177
+ useSettingsStore.setState({ graphQueryMaxDepth: depth })
178
+ }, [])
179
+
180
+ const setGraphLayoutMaxIterations = useCallback((iterations: number) => {
181
+ if (iterations < 1) return
182
+ useSettingsStore.setState({ graphLayoutMaxIterations: iterations })
183
+ }, [])
184
+
185
  const setApiKey = useCallback(async () => {
186
  useSettingsStore.setState({ apiKey: tempApiKey || null })
187
  await useBackendState.getState().check()
 
209
  onCloseAutoFocus={(e) => e.preventDefault()}
210
  >
211
  <div className="flex flex-col gap-2">
212
+ <LabeledCheckBox
213
+ checked={enableHealthCheck}
214
+ onCheckedChange={setEnableHealthCheck}
215
+ label="Health Check"
216
+ />
217
+
218
+ <Separator />
219
+
220
  <LabeledCheckBox
221
  checked={showPropertyPanel}
222
  onCheckedChange={setShowPropertyPanel}
 
260
  />
261
 
262
  <Separator />
263
+ <LabeledNumberInput
264
+ label="Max Query Depth"
265
+ min={1}
266
+ value={graphQueryMaxDepth}
267
+ onEditFinished={setGraphQueryMaxDepth}
268
+ />
269
+ <LabeledNumberInput
270
+ label="Max Layout Iterations"
271
+ min={1}
272
+ max={20}
273
+ value={graphLayoutMaxIterations}
274
+ onEditFinished={setGraphLayoutMaxIterations}
275
  />
276
 
277
  <Separator />
lightrag_webui/src/components/retrieval/ChatMessage.tsx ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReactNode, useCallback } from 'react'
2
+ import { Message } from '@/api/lightrag'
3
+ import useTheme from '@/hooks/useTheme'
4
+ import Button from '@/components/ui/Button'
5
+ import { cn } from '@/lib/utils'
6
+
7
+ import ReactMarkdown from 'react-markdown'
8
+ import remarkGfm from 'remark-gfm'
9
+ import rehypeReact from 'rehype-react'
10
+ import remarkMath from 'remark-math'
11
+
12
+ import type { Element } from 'hast'
13
+
14
+ import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
15
+ import { oneLight, oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'
16
+
17
+ import { LoaderIcon, CopyIcon } from 'lucide-react'
18
+
19
+ export type MessageWithError = Message & {
20
+ isError?: boolean
21
+ }
22
+
23
+ export const ChatMessage = ({ message }: { message: MessageWithError }) => {
24
+ const handleCopyMarkdown = useCallback(async () => {
25
+ if (message.content) {
26
+ try {
27
+ await navigator.clipboard.writeText(message.content)
28
+ } catch (err) {
29
+ console.error('Failed to copy:', err)
30
+ }
31
+ }
32
+ }, [message])
33
+
34
+ return (
35
+ <div
36
+ className={`max-w-[80%] rounded-lg px-4 py-2 ${
37
+ message.role === 'user'
38
+ ? 'bg-primary text-primary-foreground'
39
+ : message.isError
40
+ ? 'bg-red-100 text-red-600 dark:bg-red-950 dark:text-red-400'
41
+ : 'bg-muted'
42
+ }`}
43
+ >
44
+ <pre className="relative break-words whitespace-pre-wrap">
45
+ <ReactMarkdown
46
+ className="dark:prose-invert max-w-none text-base text-sm"
47
+ remarkPlugins={[remarkGfm, remarkMath]}
48
+ rehypePlugins={[rehypeReact]}
49
+ skipHtml={false}
50
+ components={{
51
+ code: CodeHighlight
52
+ }}
53
+ >
54
+ {message.content}
55
+ </ReactMarkdown>
56
+ {message.role === 'assistant' && message.content.length > 0 && (
57
+ <Button
58
+ onClick={handleCopyMarkdown}
59
+ className="absolute right-0 bottom-0 size-6 rounded-md opacity-20 transition-opacity hover:opacity-100"
60
+ tooltip="Copy to clipboard"
61
+ variant="default"
62
+ size="icon"
63
+ >
64
+ <CopyIcon />
65
+ </Button>
66
+ )}
67
+ </pre>
68
+ {message.content.length === 0 && <LoaderIcon className="animate-spin duration-2000" />}
69
+ </div>
70
+ )
71
+ }
72
+
73
+ interface CodeHighlightProps {
74
+ inline?: boolean
75
+ className?: string
76
+ children?: ReactNode
77
+ node?: Element
78
+ }
79
+
80
+ const isInlineCode = (node: Element): boolean => {
81
+ const textContent = (node.children || [])
82
+ .filter((child) => child.type === 'text')
83
+ .map((child) => (child as any).value)
84
+ .join('')
85
+
86
+ return !textContent.includes('\n')
87
+ }
88
+
89
+ const CodeHighlight = ({ className, children, node, ...props }: CodeHighlightProps) => {
90
+ const { theme } = useTheme()
91
+ const match = className?.match(/language-(\w+)/)
92
+ const language = match ? match[1] : undefined
93
+ const inline = node ? isInlineCode(node) : false
94
+
95
+ return !inline ? (
96
+ <SyntaxHighlighter
97
+ style={theme === 'dark' ? oneDark : oneLight}
98
+ PreTag="div"
99
+ language={language}
100
+ {...props}
101
+ >
102
+ {String(children).replace(/\n$/, '')}
103
+ </SyntaxHighlighter>
104
+ ) : (
105
+ <code
106
+ className={cn(className, 'mx-1 rounded-xs bg-black/10 px-1 dark:bg-gray-100/20')}
107
+ {...props}
108
+ >
109
+ {children}
110
+ </code>
111
+ )
112
+ }
lightrag_webui/src/features/RetrievalTesting.tsx CHANGED
@@ -1,38 +1,16 @@
1
  import Input from '@/components/ui/Input'
2
  import Button from '@/components/ui/Button'
3
  import { useCallback, useEffect, useRef, useState } from 'react'
4
- import { queryText, queryTextStream, Message as ChatMessage } from '@/api/lightrag'
5
  import { errorMessage } from '@/lib/utils'
6
  import { useSettingsStore } from '@/stores/settings'
7
  import { useDebounce } from '@/hooks/useDebounce'
8
  import QuerySettings from '@/components/retrieval/QuerySettings'
9
-
10
- import ReactMarkdown from 'react-markdown'
11
- import remarkGfm from 'remark-gfm'
12
- import rehypeReact from 'rehype-react'
13
- import remarkMath from 'remark-math'
14
-
15
- import { EraserIcon, SendIcon, LoaderIcon } from 'lucide-react'
16
-
17
- type Message = ChatMessage & {
18
- isError?: boolean
19
- }
20
-
21
- const ChatMessageComponent = ({ message }: { message: Message }) => {
22
- return (
23
- <ReactMarkdown
24
- className="prose lg:prose-xs dark:prose-invert max-w-none text-base"
25
- remarkPlugins={[remarkGfm, remarkMath]}
26
- rehypePlugins={[rehypeReact]}
27
- skipHtml={false}
28
- >
29
- {message.content}
30
- </ReactMarkdown>
31
- )
32
- }
33
 
34
  export default function RetrievalTesting() {
35
- const [messages, setMessages] = useState<Message[]>(
36
  () => useSettingsStore.getState().retrievalHistory || []
37
  )
38
  const [inputValue, setInputValue] = useState('')
@@ -147,22 +125,7 @@ export default function RetrievalTesting() {
147
  key={idx}
148
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
149
  >
150
- <div
151
- className={`max-w-[80%] rounded-lg px-4 py-2 ${
152
- message.role === 'user'
153
- ? 'bg-primary text-primary-foreground'
154
- : message.isError
155
- ? 'bg-red-100 text-red-600 dark:bg-red-950 dark:text-red-400'
156
- : 'bg-muted'
157
- }`}
158
- >
159
- <pre className="break-words whitespace-pre-wrap">
160
- {<ChatMessageComponent message={message} />}
161
- </pre>
162
- {message.content.length === 0 && (
163
- <LoaderIcon className="animate-spin duration-2000" />
164
- )}
165
- </div>
166
  </div>
167
  ))
168
  )}
 
1
  import Input from '@/components/ui/Input'
2
  import Button from '@/components/ui/Button'
3
  import { useCallback, useEffect, useRef, useState } from 'react'
4
+ import { queryText, queryTextStream, Message } from '@/api/lightrag'
5
  import { errorMessage } from '@/lib/utils'
6
  import { useSettingsStore } from '@/stores/settings'
7
  import { useDebounce } from '@/hooks/useDebounce'
8
  import QuerySettings from '@/components/retrieval/QuerySettings'
9
+ import { ChatMessage, MessageWithError } from '@/components/retrieval/ChatMessage'
10
+ import { EraserIcon, SendIcon } from 'lucide-react'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  export default function RetrievalTesting() {
13
+ const [messages, setMessages] = useState<MessageWithError[]>(
14
  () => useSettingsStore.getState().retrievalHistory || []
15
  )
16
  const [inputValue, setInputValue] = useState('')
 
125
  key={idx}
126
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
127
  >
128
+ {<ChatMessage message={message} />}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  </div>
130
  ))
131
  )}
lightrag_webui/src/features/SiteHeader.tsx CHANGED
@@ -55,6 +55,7 @@ export default function SiteHeader() {
55
  <header className="border-border/40 bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 flex h-10 w-full border-b px-4 backdrop-blur">
56
  <a href="/" className="mr-6 flex items-center gap-2">
57
  <ZapIcon className="size-4 text-emerald-400" aria-hidden="true" />
 
58
  <span className="font-bold md:inline-block">{SiteInfo.name}</span>
59
  </a>
60
 
 
55
  <header className="border-border/40 bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 flex h-10 w-full border-b px-4 backdrop-blur">
56
  <a href="/" className="mr-6 flex items-center gap-2">
57
  <ZapIcon className="size-4 text-emerald-400" aria-hidden="true" />
58
+ {/* <img src='/logo.png' className="size-4" /> */}
59
  <span className="font-bold md:inline-block">{SiteInfo.name}</span>
60
  </a>
61
 
lightrag_webui/src/hooks/useLightragGraph.tsx CHANGED
@@ -50,11 +50,11 @@ export type NodeType = {
50
  }
51
  export type EdgeType = { label: string }
52
 
53
- const fetchGraph = async (label: string) => {
54
  let rawData: any = null
55
 
56
  try {
57
- rawData = await queryGraphs(label)
58
  } catch (e) {
59
  useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!')
60
  return null
@@ -161,12 +161,13 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
161
  return graph
162
  }
163
 
164
- const lastQueryLabel = { label: '' }
165
 
166
  const useLightrangeGraph = () => {
167
  const queryLabel = useSettingsStore.use.queryLabel()
168
  const rawGraph = useGraphStore.use.rawGraph()
169
  const sigmaGraph = useGraphStore.use.sigmaGraph()
 
170
 
171
  const getNode = useCallback(
172
  (nodeId: string) => {
@@ -184,11 +185,13 @@ const useLightrangeGraph = () => {
184
 
185
  useEffect(() => {
186
  if (queryLabel) {
187
- if (lastQueryLabel.label !== queryLabel) {
188
  lastQueryLabel.label = queryLabel
 
 
189
  const state = useGraphStore.getState()
190
  state.reset()
191
- fetchGraph(queryLabel).then((data) => {
192
  // console.debug('Query label: ' + queryLabel)
193
  state.setSigmaGraph(createSigmaGraph(data))
194
  data?.buildDynamicMap()
@@ -200,7 +203,7 @@ const useLightrangeGraph = () => {
200
  state.reset()
201
  state.setSigmaGraph(new DirectedGraph())
202
  }
203
- }, [queryLabel])
204
 
205
  const lightrageGraph = useCallback(() => {
206
  if (sigmaGraph) {
 
50
  }
51
  export type EdgeType = { label: string }
52
 
53
+ const fetchGraph = async (label: string, maxDepth: number) => {
54
  let rawData: any = null
55
 
56
  try {
57
+ rawData = await queryGraphs(label, maxDepth)
58
  } catch (e) {
59
  useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!')
60
  return null
 
161
  return graph
162
  }
163
 
164
+ const lastQueryLabel = { label: '', maxQueryDepth: 0 }
165
 
166
  const useLightrangeGraph = () => {
167
  const queryLabel = useSettingsStore.use.queryLabel()
168
  const rawGraph = useGraphStore.use.rawGraph()
169
  const sigmaGraph = useGraphStore.use.sigmaGraph()
170
+ const maxQueryDepth = useSettingsStore.use.graphQueryMaxDepth()
171
 
172
  const getNode = useCallback(
173
  (nodeId: string) => {
 
185
 
186
  useEffect(() => {
187
  if (queryLabel) {
188
+ if (lastQueryLabel.label !== queryLabel || lastQueryLabel.maxQueryDepth !== maxQueryDepth) {
189
  lastQueryLabel.label = queryLabel
190
+ lastQueryLabel.maxQueryDepth = maxQueryDepth
191
+
192
  const state = useGraphStore.getState()
193
  state.reset()
194
+ fetchGraph(queryLabel, maxQueryDepth).then((data) => {
195
  // console.debug('Query label: ' + queryLabel)
196
  state.setSigmaGraph(createSigmaGraph(data))
197
  data?.buildDynamicMap()
 
203
  state.reset()
204
  state.setSigmaGraph(new DirectedGraph())
205
  }
206
+ }, [queryLabel, maxQueryDepth])
207
 
208
  const lightrageGraph = useCallback(() => {
209
  if (sigmaGraph) {
lightrag_webui/src/lib/constants.ts CHANGED
@@ -16,6 +16,7 @@ export const edgeColorSelected = '#F57F17'
16
  export const edgeColorHighlighted = '#B2EBF2'
17
 
18
  export const searchResultLimit = 20
 
19
 
20
  export const minNodeSize = 4
21
  export const maxNodeSize = 20
@@ -27,10 +28,39 @@ export const defaultQueryLabel = '*'
27
  // reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types/Common_types
28
  export const supportedFileTypes = {
29
  'text/plain': [
30
- '.txt', '.md', '.html', '.htm', '.tex', '.json', '.xml', '.yaml', '.yml',
31
- '.rtf', '.odt', '.epub', '.csv', '.log', '.conf', '.ini', '.properties',
32
- '.sql', '.bat', '.sh', '.c', '.cpp', '.py', '.java', '.js', '.ts',
33
- '.swift', '.go', '.rb', '.php', '.css', '.scss', '.less'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  ],
35
  'application/pdf': ['.pdf'],
36
  'application/msword': ['.doc'],
 
16
  export const edgeColorHighlighted = '#B2EBF2'
17
 
18
  export const searchResultLimit = 20
19
+ export const labelListLimit = 40
20
 
21
  export const minNodeSize = 4
22
  export const maxNodeSize = 20
 
28
  // reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/MIME_types/Common_types
29
  export const supportedFileTypes = {
30
  'text/plain': [
31
+ '.txt',
32
+ '.md',
33
+ '.html',
34
+ '.htm',
35
+ '.tex',
36
+ '.json',
37
+ '.xml',
38
+ '.yaml',
39
+ '.yml',
40
+ '.rtf',
41
+ '.odt',
42
+ '.epub',
43
+ '.csv',
44
+ '.log',
45
+ '.conf',
46
+ '.ini',
47
+ '.properties',
48
+ '.sql',
49
+ '.bat',
50
+ '.sh',
51
+ '.c',
52
+ '.cpp',
53
+ '.py',
54
+ '.java',
55
+ '.js',
56
+ '.ts',
57
+ '.swift',
58
+ '.go',
59
+ '.rb',
60
+ '.php',
61
+ '.css',
62
+ '.scss',
63
+ '.less'
64
  ],
65
  'application/pdf': ['.pdf'],
66
  'application/msword': ['.doc'],
lightrag_webui/src/stores/settings.ts CHANGED
@@ -8,9 +8,7 @@ type Theme = 'dark' | 'light' | 'system'
8
  type Tab = 'documents' | 'knowledge-graph' | 'retrieval' | 'api'
9
 
10
  interface SettingsState {
11
- theme: Theme
12
- setTheme: (theme: Theme) => void
13
-
14
  showPropertyPanel: boolean
15
  showNodeSearchBar: boolean
16
 
@@ -21,23 +19,35 @@ interface SettingsState {
21
  enableHideUnselectedEdges: boolean
22
  enableEdgeEvents: boolean
23
 
 
 
 
 
 
 
 
24
  queryLabel: string
25
  setQueryLabel: (queryLabel: string) => void
26
 
27
- enableHealthCheck: boolean
28
- setEnableHealthCheck: (enable: boolean) => void
 
 
 
29
 
 
30
  apiKey: string | null
31
  setApiKey: (key: string | null) => void
32
 
33
- currentTab: Tab
34
- setCurrentTab: (tab: Tab) => void
 
35
 
36
- retrievalHistory: Message[]
37
- setRetrievalHistory: (history: Message[]) => void
38
 
39
- querySettings: Omit<QueryRequest, 'query'>
40
- updateQuerySettings: (settings: Partial<QueryRequest>) => void
41
  }
42
 
43
  const useSettingsStoreBase = create<SettingsState>()(
@@ -55,7 +65,11 @@ const useSettingsStoreBase = create<SettingsState>()(
55
  enableHideUnselectedEdges: true,
56
  enableEdgeEvents: false,
57
 
 
 
 
58
  queryLabel: defaultQueryLabel,
 
59
  enableHealthCheck: true,
60
 
61
  apiKey: null,
@@ -81,11 +95,18 @@ const useSettingsStoreBase = create<SettingsState>()(
81
 
82
  setTheme: (theme: Theme) => set({ theme }),
83
 
 
 
 
 
 
84
  setQueryLabel: (queryLabel: string) =>
85
  set({
86
  queryLabel
87
  }),
88
 
 
 
89
  setEnableHealthCheck: (enable: boolean) => set({ enableHealthCheck: enable }),
90
 
91
  setApiKey: (apiKey: string | null) => set({ apiKey }),
@@ -102,7 +123,7 @@ const useSettingsStoreBase = create<SettingsState>()(
102
  {
103
  name: 'settings-storage',
104
  storage: createJSONStorage(() => localStorage),
105
- version: 6,
106
  migrate: (state: any, version: number) => {
107
  if (version < 2) {
108
  state.showEdgeLabel = false
@@ -137,6 +158,10 @@ const useSettingsStoreBase = create<SettingsState>()(
137
  }
138
  state.retrievalHistory = []
139
  }
 
 
 
 
140
  return state
141
  }
142
  }
 
8
  type Tab = 'documents' | 'knowledge-graph' | 'retrieval' | 'api'
9
 
10
  interface SettingsState {
11
+ // Graph viewer settings
 
 
12
  showPropertyPanel: boolean
13
  showNodeSearchBar: boolean
14
 
 
19
  enableHideUnselectedEdges: boolean
20
  enableEdgeEvents: boolean
21
 
22
+ graphQueryMaxDepth: number
23
+ setGraphQueryMaxDepth: (depth: number) => void
24
+
25
+ graphLayoutMaxIterations: number
26
+ setGraphLayoutMaxIterations: (iterations: number) => void
27
+
28
+ // Retrieval settings
29
  queryLabel: string
30
  setQueryLabel: (queryLabel: string) => void
31
 
32
+ retrievalHistory: Message[]
33
+ setRetrievalHistory: (history: Message[]) => void
34
+
35
+ querySettings: Omit<QueryRequest, 'query'>
36
+ updateQuerySettings: (settings: Partial<QueryRequest>) => void
37
 
38
+ // Auth settings
39
  apiKey: string | null
40
  setApiKey: (key: string | null) => void
41
 
42
+ // App settings
43
+ theme: Theme
44
+ setTheme: (theme: Theme) => void
45
 
46
+ enableHealthCheck: boolean
47
+ setEnableHealthCheck: (enable: boolean) => void
48
 
49
+ currentTab: Tab
50
+ setCurrentTab: (tab: Tab) => void
51
  }
52
 
53
  const useSettingsStoreBase = create<SettingsState>()(
 
65
  enableHideUnselectedEdges: true,
66
  enableEdgeEvents: false,
67
 
68
+ graphQueryMaxDepth: 3,
69
+ graphLayoutMaxIterations: 10,
70
+
71
  queryLabel: defaultQueryLabel,
72
+
73
  enableHealthCheck: true,
74
 
75
  apiKey: null,
 
95
 
96
  setTheme: (theme: Theme) => set({ theme }),
97
 
98
+ setGraphLayoutMaxIterations: (iterations: number) =>
99
+ set({
100
+ graphLayoutMaxIterations: iterations
101
+ }),
102
+
103
  setQueryLabel: (queryLabel: string) =>
104
  set({
105
  queryLabel
106
  }),
107
 
108
+ setGraphQueryMaxDepth: (depth: number) => set({ graphQueryMaxDepth: depth }),
109
+
110
  setEnableHealthCheck: (enable: boolean) => set({ enableHealthCheck: enable }),
111
 
112
  setApiKey: (apiKey: string | null) => set({ apiKey }),
 
123
  {
124
  name: 'settings-storage',
125
  storage: createJSONStorage(() => localStorage),
126
+ version: 7,
127
  migrate: (state: any, version: number) => {
128
  if (version < 2) {
129
  state.showEdgeLabel = false
 
158
  }
159
  state.retrievalHistory = []
160
  }
161
+ if (version < 7) {
162
+ state.graphQueryMaxDepth = 3
163
+ state.graphLayoutMaxIterations = 10
164
+ }
165
  return state
166
  }
167
  }