diff --git a/.claude/init b/.claude/init new file mode 100644 index 00000000..7597c975 --- /dev/null +++ b/.claude/init @@ -0,0 +1 @@ +# Initialization diff --git a/.claude/skills/init b/.claude/skills/init new file mode 100644 index 00000000..e8e95c54 --- /dev/null +++ b/.claude/skills/init @@ -0,0 +1 @@ +# Intialization diff --git a/.claude/skills/semantica/init b/.claude/skills/semantica/init new file mode 100644 index 00000000..7597c975 --- /dev/null +++ b/.claude/skills/semantica/init @@ -0,0 +1 @@ +# Initialization diff --git a/.gitignore b/.gitignore index 97b95c1b..c53c2817 100644 --- a/.gitignore +++ b/.gitignore @@ -111,5 +111,9 @@ sample_data/ # Test Results test_results.txt +# Frontend workspace artifacts +semantica-explorer/ +node_modules/ + # Frontend build artifacts (generated by Vite — do not track in git) semantica/static/ diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b6c43f..c1bc150c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- **Fix: DeepSeekProvider now uses OpenAI SDK instead of unmaintained deepseek SDK** (closes #482, PR #482 by @liling, review fixes by @KaifAhmad1): + - **Root cause**: The `deepseek` PyPI package has no `deepseek.Client`, causing `AttributeError` on every `DeepSeekProvider` instantiation. The DeepSeek API is OpenAI-compatible, so the `openai` SDK is the correct client. + - **`_init_client` rewritten**: Replaced `import deepseek; deepseek.Client(api_key=...)` with `from openai import OpenAI; OpenAI(api_key=..., base_url=self.base_url)`, matching the pattern already used by `NovitaProvider`. + - **`self.base_url` added to `__init__`**: Set to `"https://api.deepseek.com/v1"` (with `/v1` suffix required by the OpenAI SDK for correct endpoint resolution). This was missing from the original PR, causing a second `AttributeError` at `_init_client` call time. + - **`generate_typed` `verbose_mode` fix**: `verbose_mode` was referenced before assignment inside the instructor path. Added assignment `verbose_mode = kwargs.get("verbose", False) or self.config.get("verbose", False)` at the correct scope. + - **`pyproject.toml` updated**: `llm-deepseek` extra now declares `openai>=1.0.0` instead of the defunct `deepseek>=0.1.0`. + - **Warning message updated**: `_init_client` ImportError warning now references the `openai` library and `llm-openai` extra. + - **Instructor path improved**: Since `self.client` is now an `OpenAI` instance, the `isinstance(self.client, OpenAI)` check in `generate_typed` passes correctly, avoiding a redundant second client construction. + - 19 new tests in `tests/semantic_extract/test_pr482_deepseek_openai.py` across five suites: `TestDeepSeekProviderInit` (8 — covers `base_url`, OpenAI instantiation, no `deepseek` import, ImportError handling, `is_available`), `TestDeepSeekProviderGenerate` (5 — `generate`, `generate_structured`, no-client error paths), `TestDeepSeekInstructorPath` (1 — `isinstance` check), `TestVerboseModeAssignment` (4 — no NameError, verbose kwarg, config verbose, no-print default), `TestDeepSeekGenerateTypedInstructorIntegration` (1 — end-to-end instructor path reuses existing client). + +- **Performance: Indexed search for large knowledge graphs** (closes #467, PR #481 by @ZohaibHassan16, review fixes by @KaifAhmad1): + - **Root cause**: The previous `GraphSession.search()` ran a full O(n) scan over all nodes per query, serializing every node's properties to JSON for string matching. On a 118 k-node graph warm queries took 24–471 ms; a session with 500 k nodes was effectively unusable. + - **New `semantica/explorer/search_index.py`**: Purpose-built in-memory inverted index with three lookup tiers — exact-term index (full normalized strings), token index (individual words), and prefix index (2–12 character prefixes of every token). A linear secondary-scan fallback (capped at 12 k nodes) handles queries that miss all three tiers. An LRU result cache (128 slots, `OrderedDict`) serves repeat queries at zero cost. Warm query times on the same 118 k-node graph: 24 ms → 0.004 ms (exact), 471 ms → 0.009 ms (ID lookup), 475 ms → 0.002 ms (no-match). + - **`IndexedNodeDocument`** frozen dataclass stores per-node primary text (ID, content, curated alias keys: `label`, `name`, `pref_label`, `aliases`, `synonyms`, `display_name`, etc.), secondary text (remaining properties), token set, prefix expansions, confidence, and tags. Primary text prioritizes human-readable fields; secondary text covers the full property bag up to a 48-fragment cap. + - **Scoring**: exact ID match → 140, exact term → 120, primary-text substring → 78 + length bonus, token hit → 18, prefix hit → 10, multi-token bonus → 4 per hit. Ties broken deterministically on `(score, exactness, token_hits, node_id)`. + - **Mutation sync**: `GraphSession.add_node()`, `add_nodes()`, `add_edges()` and `add_node()` update the index incrementally. `handle_graph_mutation()` integrates with the WebSocket mutation bridge for live updates during reasoning, enrichment, and remote graph changes. `rebuild_search_index()` performs a full O(n) rebuild when needed (session init, merge, reload). `enrich.py` routes node/edge additions through `session.add_node`/`session.add_edge` so reasoning-inferred nodes are indexed immediately. + - **Review fixes applied**: replaced `list.sort()` per upsert with `bisect.insort()` (O(log n) vs O(n log n)); replaced `list.remove()` with `bisect.bisect_left` + `pop()` (O(log n) vs O(n)); added `with self._lock` in `handle_graph_mutation()` to prevent index races from the WebSocket thread; removed unnecessary source/target upserts on `add_edge()` (edges don't affect node text); sorted tag values in `_cache_key()` so `["a","b"]` and `["b","a"]` share a cache entry. + - **Follow-up fix by @KaifAhmad1 and @ZohaibHassan16**: restored `_ordered_node_ids` maintenance during upserts so `secondary_scan` fallback works for terms that are only present in non-curated properties; added regression test coverage to lock this behavior. + - 3 new tests: `test_search_exact_and_prefix` (exact match + prefix match), `test_search_filters_and_cache_stability` (type + confidence filter, identical repeated requests), `test_search_sees_new_nodes_after_mutation` (node added via `session.add_node()` immediately visible in search). + - 1 additional regression test: `test_search_secondary_scan_fallback_matches_non_curated_properties` (verifies fallback matching when a query term appears only in non-curated properties). +- **Fix: Provenance traversal now includes multi-hop upstream ancestors + edge direction classification** (closes #470, PR #480 by @Sameer6305, review fixes by @KaifAhmad1): + - **Bug — upstream ancestors silently excluded**: `_build_provenance()` built a directed `nx.DiGraph` and seeded first-hop neighbors correctly, but the final subgraph extraction called `nx.ego_graph(..., undirected=False)`. With directed traversal, ego-graph expansion only follows outgoing edges from the focus node, so any node that *points into* the focus node (i.e. an upstream ancestor at depth ≥ 2) was invisible. For the chain `Source → Intermediate → node_id`, `Intermediate` appeared at hop 1 but `Source` was silently dropped. Fixed by changing to `undirected=True` — the radius expansion now traverses both incoming and outgoing edges while the underlying `DiGraph` is preserved, so edge source/target semantics remain correct. + - **Enhancement — edge direction classification**: `ProvenanceEdge` gains a `direction: str` field. Each edge in the provenance subgraph is classified relative to the focus node: `"upstream"` when `target == node_id` (edge flows into the focus node), `"downstream"` when `source == node_id` (edge flows out), and `"lateral"` for all other edges between non-focus neighbors. This lets consumers distinguish ancestor provenance from descendant impact without re-traversing the graph. + - **Enhancement — grouped markdown report**: `_render_markdown()` now groups lineage edges under separate `## Upstream`, `## Downstream`, and `## Lateral` sections instead of a flat `## Lineage Edges` list. Empty sections are omitted. This improves readability of exported provenance reports. + - **Schema consolidation**: `ProvenanceNode`, `ProvenanceEdge`, and `ProvenanceResponse` moved from inline definitions in `routes/provenance.py` to the shared `semantica/explorer/schemas.py`, matching the convention used by all other Explorer routes. `ProvenanceNode.parent_id` is now `Optional[str] = None`. + - **Merge conflicts resolved**: Resolved all conflict markers in `provenance.py`, `app.py`, and `.gitignore`; restored the complete router import set in `app.py` (`graph`, `sparql`, `temporal`, `vocabulary`) that the conflict had dropped. + - 2 new tests in `tests/explorer/test_provenance_route.py`: `test_build_provenance_direction_classification_chain` (asserts `Source` and `Intermediate` both appear for `Source → Intermediate → node_id`; verifies `Intermediate → node_id` classified as `"upstream"`) and `test_render_markdown_groups_edges_by_direction` (asserts grouped section headings and correct edge lines in output). + +- **Fix: `OWLExporter._export_owl_turtle` invalid Turtle syntax and silent data-property omission** (closes #478 by @KaifAhmad1): + - **Bug 1 — Invalid Turtle syntax**: `_export_owl_turtle` unconditionally wrote `rdfs:label` with a closing period (`.`), then appended `rdfs:subClassOf`, `rdfs:domain`, and `rdfs:range` triples after the closed block. Any RDF parser would reject the output. Fixed by introducing `_ttl_block(subject_uri, rdf_type, predicates)` — all predicate-object pairs for a subject are accumulated first, then joined with ` ;\n ` and terminated with a single ` .`, producing valid Turtle in all cases. + - **Bug 2 — Data properties silently dropped**: `_export_owl_turtle` had loops for `classes` and `object_properties` but no loop for `data_properties`, so all `owl:DatatypeProperty` declarations were silently omitted. Added the missing loop, mirroring the existing object-property loop. + - **String escaping**: User-provided strings (`name`, `description`, `comment`, version) were embedded directly into Turtle string literals without escaping. A class named `John"s Class` or a comment containing a backslash or newline produced unparseable output. Added `_escape_ttl_str()` static method (escapes `"`, `\`, `\n`, `\r`, `\t`) applied at every `rdfs:label`, `rdfs:comment`, and `owl:versionInfo` site. + - **Null-check consistency**: All optional field reads now use `x = prop.get("field"); if x:` uniformly — eliminates the mixed pattern of `.get()` guards followed by direct `[]` access. + - 43 tests added in `tests/export/test_owl_exporter.py` across five suites: `TestTurtleSyntaxValidity` (5), `TestDataPropertiesInTurtle` (8), `TestTurtleHeader` (4), `TestTurtleStringEscaping` (16), `TestNullFieldHandling` (7), plus `TestObjectPropertyListDomainRange` (2) and `TestEquivalentClass` (1). + +- **Enhancement: Node distance semantics in path responses** (closes #472 by @KaifAhmad1): `PathResponse` now surfaces two new first-class fields — `hop_count: int` (equal to `len(path) - 1`; `0` for self-paths) and `distance_band: str` — so callers no longer need to count hops or implement band classification themselves. Four bands are defined: `"direct"` (0–1 hops), `"near"` (2–3), `"mid-range"` (4–6), `"distant"` (7+). The classification function `classify_path_distance()` lives in `semantica/utils/helpers.py` as the single source of truth; both the Explorer route and the visualizer import from it. `KGVisualizer.visualize_network()` gains an optional `highlight_path: list[str]` parameter: when provided, path edges are rendered as a separate orange trace with opacity and stroke width scaled to the distance band (direct: 1.0 / 4 px → distant: 0.35 / 1.5 px), while non-path edges render at reduced opacity underneath. Edge direction is respected — only the forward pairs `(A, B)` along the path are matched; reverse back-edges in directed graphs are not incorrectly highlighted. A logger warning is emitted when any node ID in `highlight_path` has no layout position, surfacing silent no-op mismatches. Frontend `PathResponse` type in `GraphInspectorPanel.tsx` and `GraphWorkspaceShell.tsx` extended with `hop_count: number` and `distance_band: "direct" | "near" | "mid-range" | "distant"`. All changes are additive; no existing fields removed. 10 new tests: 2 API-level (`test_response_includes_hop_count_and_distance_band`, `test_one_hop_path_is_direct`) and 8 unit tests covering all four band boundaries (0, 1, 2, 3, 4, 6, 7, 20 hops). + +- **Enhancement: Bidirectional path finding in Knowledge Explorer** (closes #469 by @KaifAhmad1): Path queries in the Explorer were direction-sensitive — querying B→A when only the edge A→B existed always returned no result, because `PathFinder._get_neighbors()` called `graph.neighbors(node)` which on a `nx.DiGraph` yields only successors. Added a `directed: bool = True` parameter to `bfs_shortest_path()` and `dijkstra_shortest_path()`. When `directed=False` a lightweight undirected view is built via `graph.to_undirected()` for the traversal pass only; the original directed edges are preserved and returned in the response. A `_make_undirected_view()` helper encapsulates the conversion and falls back safely for non-NetworkX graph types. The `/api/graph/node/{id}/path` route exposes the parameter as a query string flag (`?directed=false`); `PathResponse` gains a `directed: bool` field that echoes the mode used. Default is `True`, so all existing callers are unaffected. The route also gained an empty-path 404 guard — previously a traversal that found no path returned `200` with `path: []` instead of `404`. 21 new tests: 12 unit tests in `TestBidirectionalPathFinding` (`tests/kg/test_path_finder.py`) and 9 API-level tests in `TestBidirectionalPathRoute` (`tests/explorer/test_explorer_api.py`). + +- **Enhancement: Native `KnowledgeGraph` type support in `KGVisualizer`** (PR `kg` by @KaifAhmad1, closes #471): Added `semantica/kg/knowledge_graph.py` — a formal `KnowledgeGraph` dataclass (`entities`, `relationships`, `metadata`) that is now the canonical in-memory type produced and consumed by the Semantica KG pipeline. Exported from `semantica.kg`. `KGVisualizer` gains `_convert_knowledge_graph()` — an authoritative, non-mutating conversion path from `KnowledgeGraph` to the internal dict format — and `_normalize_graph()` now routes `isinstance(graph, KnowledgeGraph)` through it as an explicit fast-path before duck-typing. All five public entry points (`visualize_network`, `visualize_communities`, `visualize_centrality`, `visualize_entity_types`, `visualize_relationship_matrix`) accept `KnowledgeGraph` directly; no manual conversion required. All existing callers passing dicts or duck-typed objects are unaffected. 15 new tests in `TestFormalKnowledgeGraphType` (conversion shape, non-mutation, determinism, routing, all five entry points, import availability). +- **Fix: `KGVisualizer` now accepts `KnowledgeGraph` objects in all `visualize_*` methods** (PR `visualization` by @KaifAhmad1, closes #458): All five public methods (`visualize_network`, `visualize_communities`, `visualize_centrality`, `visualize_entity_types`, `visualize_relationship_matrix`) previously called `graph.get("entities", [])`, silently producing no output when passed a non-dict object. Added `_normalize_graph()` which duck-types the input — dicts pass through unchanged; any object exposing `.entities` / `.relationships` attributes (e.g. the result of `GraphBuilder.build()`) is converted to the canonical dict form; anything else raises a clear `ProcessingError` naming the offending type. 21 tests added in `tests/visualization/test_kg_visualizer_normalize_graph.py`. + - **Security: 12 vulnerability fixes across CRITICAL → LOW severity** (PR `security-enhancement` by @KaifAhmad1): **Critical** diff --git a/README.md b/README.md index 436762fa..1eaec608 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ [![CI](https://github.com/Hawksight-AI/semantica/workflows/CI/badge.svg)](https://github.com/Hawksight-AI/semantica/actions) [![Discord](https://img.shields.io/badge/Discord-Join%20Community-5865F2?logo=discord&logoColor=white)](https://discord.gg/sV34vps5hH) [![X](https://img.shields.io/badge/X-Follow%20Semantica-black?logo=x&logoColor=white)](https://x.com/BuildSemantica) +[![OpenClaw](https://img.shields.io/badge/OpenClaw-Plugin-FF3B30?logo=github&logoColor=white)](https://openclaw.ai) ### ⭐ Give us a Star · 🍴 Fork us · 💬 Join our Discord · 🐦 Follow on X @@ -45,7 +46,7 @@ Semantica is the **context and intelligence layer** you add on top of your exist - ✅ **Reasoning Engines** — forward chaining, Rete networks, deductive, abductive, and SPARQL. Explainable paths, not black boxes. - ✅ **Quality & Deduplication** — conflict detection, entity resolution, and pipeline validation built in. -> Works alongside LangChain, LlamaIndex, AutoGen, CrewAI, and any LLM — Semantica is the **accountability layer** on top, not a replacement. +> Works alongside **Agno** and any LLM — Semantica is the **accountability layer** on top, not a replacement. LangChain, LangGraph, CrewAI, and more coming soon. ```bash pip install semantica @@ -53,17 +54,314 @@ pip install semantica --- -## Plugins (Claude, Cursor, Codex) +## 🔌 Works With Every AI Tool -Semantica includes a cross-platform plugin bundle under `plugins/` for community use: +Semantica ships **native plugin bundles** for Claude Code, Cursor, and Codex, an **MCP server** (`python -m semantica.mcp_server`) for Windsurf, Cline, Continue, VS Code, Claude Desktop, and OpenClaw, and a **REST API** (FastAPI, port 8000) for any other tool. -- 17 domain skills (context graphs, decision intelligence, explainability, reasoning, provenance, ontology, temporal, visualization) -- Specialized agents (`decision-advisor`, `explainability`, `kg-assistant`) -- Hook configuration and platform-specific manifests for Claude, Cursor, and Codex + -See the community setup guide: + + + + + + + + + + + + + + + -- [`plugins/.claude-plugin/README.md`](plugins/.claude-plugin/README.md) + + + + + + + + + + + + + + + + + + + + + + + + +
🔌 Native Plugin Bundle⚡ MCP Server + Plugin
+Claude Code
+Claude Code
+17 skills · 3 agents · hooks +
+Cursor
+Cursor
+17 skills · 3 agents +
+Codex CLI
+Codex CLI
+17 skills · 3 agents +
+Windsurf
+Windsurf
+plugin +
+Cline
+Cline
+plugin +
+Continue
+Continue
+plugin +
+VS Code
+VS Code
+plugin +
+OpenClaw
+OpenClaw
+MCP + plugin +
☁️ MCP Server🌐 REST API
+Claude Desktop
+Claude Desktop
+MCP server +
+GitHub Copilot
+GitHub Copilot
+REST API +
+Roo Code
+Roo Code
+REST API +
+Goose
+Goose
+REST API +
+Kilo Code
+Kilo Code
+REST API +
+Aider
+Aider
+REST API +
+Amazon Q
+Amazon Q
+REST API +
+Zed
+Zed
+REST API +
🔧 Any Tool
+REST API
+Any agent
+109 REST endpoints · FastAPI · port 8000 +
+ +### Agentic Frameworks + +Semantica integrates with **Agno** today. Coming soon: LangChain, LangGraph, CrewAI, LlamaIndex, AutoGen, OpenAI Agents SDK, Google ADK, and more. + + + + + + + + + + + + + + + + + + + + +
✅ Supported
+Agno
+Agno
+First-class · pip install semantica[agno] +
🔜 Coming Soon
+LangChain
+LangChain
+Coming soon +
+LangGraph
+LangGraph
+Coming soon +
+CrewAI
+CrewAI
+Coming soon +
+LlamaIndex
+LlamaIndex
+Coming soon +
+AutoGen
+AutoGen
+Coming soon +
+OpenAI Agents SDK
+OpenAI Agents
+Coming soon +
+Google ADK
+Google ADK
+Coming soon +
+ +> **Agno — First-Class Integration** · `pip install semantica[agno]` +> +> Five integration modules live in [`integrations/agno/`](integrations/agno/): +> +> | Module | Class | What it does | +> |---|---|---| +> | `context_store.py` | `AgnoContextStore` | Graph-backed agent memory — store and retrieve structured context | +> | `knowledge_graph.py` | `AgnoKnowledgeGraph` | Implements Agno's `AgentKnowledge` protocol; full extraction pipeline | +> | `decision_kit.py` | `AgnoDecisionKit` | 6 decision-intelligence tools for Agno agents | +> | `kg_toolkit.py` | `AgnoKGToolkit` | 7 KG pipeline tools (build, query, enrich, export) | +> | `shared_context.py` | `AgnoSharedContext` | Shared context graph for multi-agent team coordination | + +### Plugin Bundles (Claude Code · Cursor · Codex) + +Native plugin bundles live under [`plugins/`](plugins/). Each directory contains a `plugin.json`, `marketplace.json`, and `README.md`. + +| Bundle | Directory | Tools | +|---|---|---| +| Claude Code | [`plugins/.claude-plugin/`](plugins/.claude-plugin/) | 17 skills · 3 agents · hooks | +| Cursor | [`plugins/.cursor-plugin/`](plugins/.cursor-plugin/) | 17 skills · 3 agents · hooks | +| Codex CLI | [`plugins/.codex-plugin/`](plugins/.codex-plugin/) | 17 skills · 3 agents | +| Windsurf | [`plugins/.windsurf-plugin/`](plugins/.windsurf-plugin/) | 17 skills · 3 agents · MCP config | +| Cline | [`plugins/.cline-plugin/`](plugins/.cline-plugin/) | 17 skills · 3 agents · MCP config | +| Continue | [`plugins/.continue-plugin/`](plugins/.continue-plugin/) | 17 skills · 3 agents · MCP config | +| VS Code | [`plugins/.vscode-plugin/`](plugins/.vscode-plugin/) | 17 skills · 3 agents · MCP config | +| OpenClaw | [`plugins/.openclaw-plugin/`](plugins/.openclaw-plugin/) | 17 skills · 3 agents · MCP config | + +**17 domain skills:** + +| Skill | What it does | +|---|---| +| `extract` | Full semantic extraction pipeline: NER, relations, events, coreference, triplets | +| `ingest` | Data ingestion from files, databases, APIs, streams, and MCP servers | +| `query` | SPARQL, Cypher, keyword search, structured graph patterns | +| `ontology` | Schema management, concepts, relationships, alignments | +| `validate` | Pipeline, extraction, schema, and ontology validation | +| `deduplicate` | Duplicate detection and entity merging with fuzzy matching | +| `embed` | Node2Vec embeddings, similarity scoring, link prediction | +| `reason` | Deductive, abductive, Datalog, SPARQL, and Rete reasoning engines | +| `decision` | Record, query, and analyze decisions; find precedents; causal analysis | +| `causal` | Cause-effect chains, interventions, counterfactuals, causal influence | +| `temporal` | Point-in-time queries, snapshots, timelines, temporal causal analysis | +| `provenance` | Data lineage, source attribution, audit trails | +| `policy` | Policy definition, enforcement, compliance checks, access control | +| `explain` | Decision logic transparency, causal context, audit-ready explanations | +| `export` | Multi-format export: JSON, RDF, Parquet, CSV, GraphML | +| `change` | Graph change tracking, diffs, temporal updates, impact analysis | +| `visualize` | Topology, centrality, communities, paths, embeddings, decision graphs | + +**3 specialized agents:** + +| Agent | Role | +|---|---| +| `kg-assistant` | General-purpose KG-aware assistant — knows all APIs and method signatures | +| `decision-advisor` | Decision intelligence specialist: causal reasoning, precedents, policy violations | +| `explainability` | Reasoning transparency specialist — generates audit-ready explanation reports | + +**Hooks** (`plugins/hooks/hooks.json`) — `PreToolUse` / `PostToolUse` matchers for syntax validation and automated warnings. + +→ [`plugins/.claude-plugin/README.md`](plugins/.claude-plugin/README.md) + +### MCP Server (expose Semantica to any MCP-aware tool) + +Semantica ships a full **MCP server** (`semantica/mcp_server.py`) — run it once and any MCP-compatible tool connects automatically: + +```bash +python -m semantica.mcp_server +``` + +Add to your tool's config (Claude Desktop, Windsurf, Cline, Continue, VS Code, Roo Code): + +```json +{ + "mcpServers": { + "semantica": { + "command": "python", + "args": ["-m", "semantica.mcp_server"] + } + } +} +``` + +**12 tools exposed:** `extract_entities`, `extract_relations`, `record_decision`, `query_decisions`, `find_precedents`, `get_causal_chain`, `add_entity`, `add_relationship`, `run_reasoning`, `get_graph_analytics`, `export_graph`, `get_graph_summary` + +**3 resources:** `semantica://graph/summary`, `semantica://decisions/list`, `semantica://schema/info` + +See [`plugins/.claude-plugin/README.md`](plugins/.claude-plugin/README.md) for per-tool config snippets. + +### MCP Client (Ingest from MCP Servers) + +Semantica also includes an **MCP client** (`semantica/ingest/mcp_client.py`) that lets you pull data from any Python/FastMCP server into a knowledge graph: + +```python +from semantica.ingest import MCPClient + +client = MCPClient("http://your-mcp-server:8080") +resources = client.list_resources() # discover available resources +data = client.read_resource("resource://your-data") +``` + +Supported connection schemes: `http://`, `https://`, `mcp://`, `sse://` · JSON-RPC · auth support · dynamic capability discovery. + +--- + +## 🖥️ Semantica Knowledge Explorer + +A real-time visual interface for exploring every dimension of your knowledge graph — built into the repo under [`explorer/`](explorer/). + +| Workspace | What you can do | +|---|---| +| **Knowledge Graph** | Pan, zoom, and inspect a live Sigma.js graph canvas with ForceAtlas2 layout | +| **Timeline** | Scrub through temporal events and watch the graph evolve | +| **Decisions** | Browse the causal chain behind every recorded decision with outcome badges | +| **Registry** | Live audit log of every graph mutation — add-node, add-edge, merge, delete | +| **Entity Resolution** | Review and merge duplicate entities detected by the deduplication engine | +| **KG Overview** | Aggregate stats, community breakdown, centrality heatmap | +| **Ontology** | SKOS/OWL vocabulary hierarchy and auto-generated schema summary | + +### Run locally + +```bash +# 1. Start the Semantica backend (port 8000) +python -m semantica.server + +# 2. In a second terminal +cd explorer +npm install +npm run dev +``` + +Open **http://localhost:5173** — the Explorer connects automatically. All `/api` and `/ws` traffic is proxied to `127.0.0.1:8000` by Vite, so no CORS configuration is needed. + +> **Requirements:** Node 18+ · Python 3.8+ · npm 9+ + +For the full setup guide, troubleshooting, and production build instructions see [`explorer/README.md`](explorer/README.md). --- @@ -347,6 +645,7 @@ Semantic memory with hybrid search and metadata filtering. | `semantica.change_management` | Version storage, change tracking, checksums, audit trails, compliance support for KGs and ontologies | | `semantica.triplet_store` | RDF triplet store integration — Blazegraph, Jena, RDF4J; SPARQL queries and bulk loading | | `semantica.visualization` | Interactive and static visualization of KGs, ontologies, embeddings, analytics, and temporal graphs | +| [`explorer/`](explorer/) | **Semantica Knowledge Explorer** — React 19 + Sigma.js UI: graph canvas, decision viewer, causal chains, entity resolution, ontology browser, and registry audit log | | `semantica.seed` | Seed data management for initial KG construction from CSV, JSON, databases, and APIs | | `semantica.core` | Framework orchestration, configuration management, knowledge base construction, plugin system | | `semantica.llms` | LLM provider integrations — Groq, OpenAI, Novita AI, HuggingFace, LiteLLM | @@ -643,16 +942,13 @@ if result.valid: - **`semantica.triplet_store`** — Blazegraph, Jena, RDF4J; SPARQL, bulk loading, SKOS helpers - **`semantica.visualization`** — KG, ontology, embedding, and temporal graph visualization - **`semantica.llms`** — Groq, OpenAI, Novita AI, HuggingFace, LiteLLM - - ---- - -## 🔌 Integrations +- **[`explorer/`](explorer/)** — **Semantica Knowledge Explorer** — browser UI for live graph inspection, decisions, entity resolution, and ontology browsing (`npm run dev` in `explorer/`) ### Graph Databases -- **AWS Neptune** — Amazon Neptune with IAM authentication +- **Neo4j** — Cypher queries via `semantica.graph_store` +- **FalkorDB** — native support; `DecisionQuery` and `CausalChainAnalyzer` work directly with FalkorDB row/header shapes - **Apache AGE** — PostgreSQL + openCypher via SQL -- **FalkorDB** — native support for decision queries and causal analysis +- **AWS Neptune** — Amazon Neptune with IAM authentication ### Vector Databases - **FAISS** — built-in, zero extra dependencies @@ -668,22 +964,15 @@ if result.valid: - **Databases** — SQL via `DBIngestor` - **Snowflake** — table/query ingestion, pagination, password/key-pair/OAuth/SSO auth · `pip install semantica[db-snowflake]` - **Docling** — advanced table and layout extraction (PDF, DOCX, PPTX, XLSX) +- **Email** — inbox ingestion via `EmailIngestor` +- **Repositories** — Git repo ingestion for code graph construction ### LLM Providers - **LiteLLM** — 100+ models: OpenAI, Anthropic, Cohere, Mistral, Ollama, Azure, AWS Bedrock, and more - **Novita AI** — OpenAI-compatible (`deepseek/deepseek-v3.2` and more) · set `NOVITA_API_KEY` - -### Agentic Frameworks -Semantica complements — not replaces — LangChain, LlamaIndex, AutoGen, CrewAI, Google ADK, and more. - -> **Agno — First-Class Integration** · `pip install semantica[agno]` -> -> Five ready-to-use Agno components: -> - `AgnoContextStore` — graph-backed agent memory -> - `AgnoKnowledgeGraph` — multi-hop GraphRAG knowledge base -> - `AgnoDecisionKit` — 6 decision-intelligence tools -> - `AgnoKGToolkit` — 7 KG pipeline tools -> - `AgnoSharedContext` — shared context graph for multi-agent teams +- **Groq** — ultra-low latency inference · set `GROQ_API_KEY` +- **HuggingFace** — local and hosted models via `HuggingFaceProvider` +- **Ollama** — local models including remote server support --- diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md deleted file mode 100644 index a028313a..00000000 --- a/RELEASE_NOTES.md +++ /dev/null @@ -1,282 +0,0 @@ -# Semantica v0.3.0 — Release Notes - -**Released:** 2026-03-10 -**PyPI:** `pip install semantica` -**Tag:** [v0.3.0](https://github.com/Hawksight-AI/semantica/releases/tag/v0.3.0) -**Classification:** Production/Stable - -> First stable, full public release of Semantica. Covers everything shipped across three release stages: 0.3.0-alpha (2026-02-19), 0.3.0-beta (2026-03-07), and 0.3.0 stable (2026-03-10). - ---- - -## Contributors - -| Contributor | Role | -|------------|------| -| [@KaifAhmad1](https://github.com/KaifAhmad1) | Lead maintainer — context graph, decision intelligence, KG algorithms, semantic extraction, pipeline, provenance, bug fixes, release management | -| [@ZohaibHassan16](https://github.com/ZohaibHassan16) | Deduplication v2 suite (candidate generation, two-stage scoring, semantic dedup), incremental/delta processing, benchmark suite | -| [@Sameer6305](https://github.com/Sameer6305) | Apache AGE backend, PgVector store, Snowflake connector, Apache Arrow export | -| [@tibisabau](https://github.com/tibisabau) | ArangoDB AQL export, Apache Parquet export | -| [@d4ndr4d3](https://github.com/d4ndr4d3) | ResourceScheduler deadlock fix | - ---- - -## v0.3.0 — Stable (2026-03-10) - -### Context Graph Feature Completeness - -**Temporal Validity Windows** (by @KaifAhmad1) - -Nodes and edges now carry first-class `valid_from` / `valid_until` ISO datetime fields. These are stored directly on `ContextNode` and `ContextEdge` dataclasses — not in metadata — and survive full serialisation round-trips through `save_to_file()` / `load_from_file()` and `to_dict()` / `from_dict()`. - -- `ContextNode.is_active(at_time=None)` and `ContextEdge.is_active(at_time=None)` — returns `True` if the node/edge is live at the given time (defaults to now). Handles both tz-aware and tz-naive datetime inputs correctly. -- `ContextGraph.find_active_nodes(node_type=None, at_time=None)` — filters the entire graph and returns only nodes within their validity window. -- `add_node(valid_from=..., valid_until=...)` and `add_edge(valid_from=..., valid_until=...)` — pass validity fields directly in the call signature. -- Bug fix: `is_active()` previously crashed with `TypeError` when passed a tz-aware `datetime` (e.g. `datetime.now(timezone.utc)`). Fixed by normalising all inputs to tz-naive UTC via a new `_parse_iso_dt()` helper. -- Bug fix: validity fields were silently lost in `add_nodes()`, `add_edges()`, `to_dict()`, and `from_dict()`. All four paths now correctly preserve and restore them. - -**Weighted Multi-Hop BFS** (by @KaifAhmad1) - -`ContextGraph.get_neighbors(node_id, hops=1, relationship_types=None, min_weight=0.0)` now accepts a `min_weight` threshold. Any edge with weight below the threshold is skipped during BFS traversal, allowing callers to confine multi-hop queries to high-confidence causal links. Default `0.0` is fully backward-compatible. - -**Cross-Graph Navigation** (by @KaifAhmad1) - -Separate `ContextGraph` instances can now be linked and navigated between — hierarchically, like separate knowledge domains that reference each other. - -- `link_graph(other_graph, source_node_id, target_node_id, link_type="CROSS_GRAPH") -> str` — creates a navigable bridge and returns a `link_id`. Records a dedicated `"cross_graph_link"` typed marker node internally (not a phantom `"entity"`) and a marker edge. -- `navigate_to(link_id) -> (other_graph, target_node_id)` — jumps to the target graph and entry node for a given link. -- `graph_id` field — each `ContextGraph` now carries a stable UUID so instances can identify each other across save/load. -- `save_to_file()` — now writes a `links` section alongside nodes and edges, containing `link_id`, `source_node_id`, `target_node_id`, and `other_graph_id` for every cross-graph link. -- `load_from_file()` — restores `graph_id` and populates `_unresolved_links` from the `links` section. -- `resolve_links(registry: Dict[str, ContextGraph]) -> int` — reconnects unresolved links post-load. Pass `{graph_id: graph_instance}` for each linked graph; returns the count of successfully resolved links. `navigate_to()` raises a clear `KeyError` with a `resolve_links()` hint if called before resolution. -- Bug fix: the previous implementation auto-created the synthetic marker target as an `"entity"` node (phantom pollution). Fixed by explicitly pre-creating a `"cross_graph_link"` typed `ContextNode` before the marker edge. -- 14 new tests in `tests/context/test_cross_graph_navigation.py` covering all scenarios including full save/load round-trips with partial registry resolution. - -**Other Fixes** (by @KaifAhmad1) - -- `PipelineBuilder.add_step()` return type annotation corrected from `"PipelineBuilder"` to `"PipelineStep"` — the implementation was already correct; only the annotation and docstring were stale. -- `test_hybrid_search_performance` timing computation fixed — now accumulates a true `search_times` list instead of reusing the last loop iteration's `start_time`; threshold relaxed to `< 5.0s` for real `sentence-transformers` (384-dim) latency on development machines. - -**Test Coverage Added** - -- 14 cross-graph navigation tests (`tests/context/test_cross_graph_navigation.py`) -- **Total: 335 context tests, 886+ tests across all modules — 0 failures** - ---- - -## v0.3.0-beta — Beta (2026-03-07) - -### Semantic Extraction Fixes - -**Multi-Founder LLM Extraction & Reasoner Inference Fix** (PR #354, by @KaifAhmad1) - -- `_parse_relation_result` in `methods.py` — unmatched subjects/objects now produce a synthetic `UNKNOWN` entity instead of silently dropping the relation. All co-founders returned by the LLM are preserved in the output. -- Duplicate relation fix — an orphaned legacy block that appended every relation twice has been removed. -- `extraction_method` parameter added — typed extraction paths now correctly record `"llm_typed"` in relation metadata instead of `"llm"`. -- `_match_pattern` in `reasoner.py` rewritten — splits patterns on `?var` placeholders first, then escapes only literal segments. Pre-bound variables resolve to exact literals, repeated variables use backreferences, non-greedy `.+?` prevents over-consumption of separators. -- Added `tests/reasoning/test_reasoner.py` (4 tests) and `tests/semantic_extract/test_relation_extractor.py` (6 tests). - -**TTL Export Alias Fix** (PR #355, by @KaifAhmad1) - -- `RDFExporter` now accepts `"ttl"`, `"nt"`, `"xml"`, `"rdf"`, and `"json-ld"` as format aliases in `export_to_rdf()`. Aliases resolve before format validation — zero public API changes. -- Added `tests/export/test_rdf_exporter.py` (8 tests). - -### Incremental / Delta Processing - -**Native Delta Computation** (PR #349, by @ZohaibHassan16, reviewed and fixed by @KaifAhmad1) - -- Native SPARQL-based diff between graph snapshots — only changed triples flow through the pipeline. -- `delta_mode` configuration in `PipelineBuilder` for near-real-time workloads. -- Version snapshot management with graph URI tracking and metadata storage. -- `prune_versions()` for automatic snapshot retention cleanup. -- Bug fixes: corrected SPARQL variable order, fixed class references, resolved duplicate dictionary keys. - -### Deduplication v2 - -**Candidate Generation v2** (PR #338, by @ZohaibHassan16) - -- New opt-in strategies: `blocking_v2` and `hybrid_v2`, replacing O(N²) pair enumeration. -- Multi-key blocking with normalised token prefixes, type-aware keys, and optional phonetic (Soundex) blocking. -- Deterministic `max_candidates_per_entity` budgeting with stable sorting. -- **63.6% faster** in worst-case scenarios (0.259s → 0.094s for 100 entities). - -**Two-Stage Scoring Prefilter** (PR #339, by @ZohaibHassan16) - -- Fast gates for type mismatch, name-length ratio, and token overlap eliminate expensive semantic scoring for obvious non-matches. -- Configurable thresholds: `min_length_ratio`, `min_token_overlap_ratio`, `required_shared_token`. -- **18–25% faster** batch processing with prefilter enabled (`prefilter_enabled=False` by default). - -**Semantic Relationship Deduplication v2** (PR #340, by @ZohaibHassan16, fixes by @KaifAhmad1) - -- Canonicalisation engine with predicate synonym mapping (`works_for` → `employed_by`). -- O(1) hash matching for exact canonical signatures. -- Weighted scoring: 60% predicate + 40% object composition with explainable `semantic_match_score`. -- **6.98x faster** than legacy mode (83ms vs 579ms). -- `dedup_triplets()` infinite recursion bug fixed; function is now a first-class API in `methods.py`. - -**Deduplication v2 Migration Guide** (PR #344, by @ZohaibHassan16, fixes by @KaifAhmad1) - -- Comprehensive `MIGRATION_V2.md` documenting all v2 strategies with code examples. -- Full backward compatibility maintained — legacy mode remains the default. - -### Export Formats - -**ArangoDB AQL Export** (PR #342, by @tibisabau) - -- Full AQL INSERT statement generation for vertices and edges. -- Configurable collection names with validation and sanitisation; batch processing (default: 1000). -- `export_arango()` convenience function; `.aql` auto-detection in the unified exporter. -- 17 tests, 100% pass rate. - -**Apache Parquet Export** (PR #343, by @tibisabau) - -- Columnar storage format with configurable compression: snappy, gzip, brotli, zstd, lz4, none. -- Explicit Apache Arrow schemas with type safety; field normalisation for varied naming conventions. -- `export_parquet()` convenience function; `.parquet` auto-detection. -- Analytics-ready for pandas, Spark, Snowflake, BigQuery, Databricks. -- 25 tests, 100% pass rate. - -### Bug Fixes & Test Suite Stabilisation - -**Test Suite Fixes** (by @KaifAhmad1) - -Context module: -- `retrieve_decision_precedents` — gated entity extraction on `use_hybrid_search=True` correctly. -- `_extract_entities_from_query` — now uses `word[0].isupper()` to capture camelCase identifiers like `CreditCard`. -- Added missing `expand_context` (BFS traversal) and `_get_decision_query` methods. -- Fixed `hybrid_retrieval`, `dynamic_context_traversal`, and `multi_hop_context_assembly` for correct single-pass BFS. -- Fixed `_retrieve_from_vector` fallback to `result["metadata"]["content"]` to prevent empty content and negative re-ranking scores. - -KG module: -- `calculate_pagerank` — added `alpha`/`max_iter` aliases; return format changed to `{"centrality": scores, "rankings": sorted_list}`. -- `community_detector._to_networkx` — now returns a NetworkX graph directly when one is passed (previously lost all edges). -- Added 9 domain-specific tracking methods to `AlgorithmTrackerWithProvenance`. -- Created `provenance_tracker.py` with `ProvenanceTracker` (`track_entity`, `get_all_sources`, `clear`). - -Pipeline module: -- Retry loop fixed — now correctly iterates to `max_retries`. -- `RecoveryAction` dataclass and `handle_failure(error, policy, retry_count)` added with LINEAR, EXPONENTIAL, and FIXED strategies. -- `add_step()` fixed to return the created `PipelineStep`. -- `validate` added as alias for `validate_pipeline` in `PipelineValidator`. - -Other: -- Fixed `NameError` for missing `Type` import in `utils/helpers.py`. -- Vector store performance threshold relaxed (100ms → 500ms per decision for development machines). -- Windows cp1252 encoding fix in test files (emoji → ASCII). -- `ProvenanceTracker` added to `semantica/kg/__init__.py` exports. - -**Results: ~840 tests passing, 36 skipped (external services), 0 failed** - ---- - -## v0.3.0-alpha — Alpha (2026-02-19) - -### Context & Decision Intelligence - -**Context Engineering Enhancement** (PR #307, by @KaifAhmad1) - -The foundational 0.3.0 feature — complete overhaul of the context module for production-grade decision intelligence: - -- Full decision lifecycle: `record_decision()` → `add_decision()` → `add_causal_relationship()` → `trace_decision_chain()` → `analyze_decision_impact()` → `analyze_decision_influence()` → `find_similar_decisions()` -- `AgentContext` unified wrapper with granular feature flags: `decision_tracking`, `kg_algorithms`, `graph_expansion`; methods: `store()`, `retrieve()`, `get_conversation_history()`, `get_statistics()`, `capture_cross_system_inputs()` -- `AgentMemory` with working, conversation, and long-term memory tiers -- `PolicyEngine` with versioned policy nodes, compliance checking (`check_decision_rules()`), and `PolicyException` model -- Hybrid precedent search combining vector, structural, and category similarity with configurable weights -- Decision influence analysis via centrality measures and causal chain tracking -- GraphStore validation preventing runtime failures; secure logging -- 9 critical bug fixes across logging, security, audit trails, API compatibility, Cypher queries, centrality access, validation - -**Context Decision Tracking Fixes** (PR #315, by @KaifAhmad1) - -- Fixed empty/None decision ID handling in `add_decision()` -- Fixed None metadata handling preventing `TypeError` -- Fixed causal chain depth logic and node exclusion -- Fixed nonexistent node handling in `add_causal_relationship()` -- Fixed precedent search direction in `find_precedents()` -- Added missing `properties` field in `to_dict()`; added `from_dict()` method -- Fixed UUID generation across all decision models -- All 71 context tests passing - -### Knowledge Graph Algorithms - -**Improved Graph Algorithms** (PR #292, by @KaifAhmad1) - -- 30+ graph algorithms across 7 categories -- Node embeddings: Node2Vec, DeepWalk, Word2Vec via `NodeEmbedder` -- Similarity: cosine, Euclidean, Manhattan, Correlation via `SimilarityCalculator` -- Path finding: Dijkstra, A*, BFS, K-shortest paths via `PathFinder` -- Link prediction: preferential attachment, Jaccard, Adamic-Adar via `LinkPredictor` -- Centrality: degree, betweenness, closeness, PageRank via `CentralityAnalyzer` -- Community detection: Louvain, Leiden, label propagation via `CommunityDetector` -- Connectivity: components, bridges, density via `ConnectivityAnalyzer` -- `GraphBuilderWithProvenance` and `AlgorithmTrackerWithProvenance` with full execution metadata - -**Improved Vector Store for Decision Tracking** (PR #293, by @KaifAhmad1) - -- `DecisionEmbeddingPipeline` with semantic and structural embeddings -- `HybridSimilarityCalculator` with configurable weights (semantic: 0.7, structural: 0.3) -- `ContextRetriever` with multi-hop reasoning -- Convenience API: `quick_decision()`, `find_precedents()`, `explain()`, `similar_to()`, `batch_decisions()`, `filter_decisions()` -- 34+ tests; performance: 0.028s per decision, 0.031s search, ~0.8KB memory per decision - -### Graph Database Backends - -**Apache AGE Backend Security Fixes** (PR #311, by @Sameer6305, fixes by @KaifAhmad1) - -- `AgeStore` class with `GraphStore` API compatibility (openCypher via SQL on PostgreSQL) -- SQL injection vulnerabilities fixed with input validation -- psycopg2-binary dependency and migration guide added -- Fixed parameter replacement and test mock leakage - -**PgVector Store Support** (PR #303, by @Sameer6305, @KaifAhmad1) - -- Native PostgreSQL vector storage using the pgvector extension -- Multiple distance metrics: cosine, L2/Euclidean, inner product -- HNSW and IVFFlat indexing for approximate nearest neighbour search -- JSONB metadata storage with flexible filtering; batch operations -- Connection pooling with psycopg3/psycopg2 fallback -- SQL injection protection via `psycopg_sql.SQL()`; idempotent index and table management -- 36+ tests with Docker integration - -### Infrastructure - -**ResourceScheduler Deadlock Fix** (PR #299, #301, by @d4ndr4d3, @KaifAhmad1) - -- Replaced `threading.Lock()` with `threading.RLock()` to fix nested lock acquisition deadlock in `allocate_resources()` -- Added `ValidationError` when no resources can be allocated -- Progress tracking updates moved outside lock scope -- 6 regression tests for deadlock prevention - -**Security Configuration** (by @KaifAhmad1) - -- Dependabot bi-weekly security updates with manual review -- Automated security scans (Bandit, Safety, Semgrep) on schedule -- Security-critical package grouping; zero auto-merge policy - ---- - -## Summary by the Numbers - -| Metric | Value | -|--------|-------| -| Total tests passing | **886+** | -| Test failures | **0** | -| Context tests | 335 | -| KG tests | ~430 | -| Semantic extraction tests | 70 (9 skipped — external LLM APIs) | -| Reasoning tests | 19 | -| Real-world scenario tests | 85 | -| PyPI classifier | Production/Stable | -| Python support | 3.8 – 3.12 | - ---- - -## Upgrade - -```bash -pip install --upgrade semantica -``` - -No breaking changes. All new parameters have safe defaults and all new methods are additive. - -See [CHANGELOG.md](CHANGELOG.md) for the full line-by-line diff. diff --git a/STRATEGIES_SUMMARY.md b/STRATEGIES_SUMMARY.md deleted file mode 100644 index 799bd01e..00000000 --- a/STRATEGIES_SUMMARY.md +++ /dev/null @@ -1,105 +0,0 @@ -# Deduplication & Conflict Resolution Strategies Summary - -## Quick Reference by Use Case - -| Use Case | Deduplication Method | Merge Strategy | Conflict Detection | Conflict Resolution | -|----------|---------------------|----------------|-------------------|---------------------| -| **Finance** | -| `01_Financial_Data_Integration_MCP` | `DuplicateDetector` (incremental) | `keep_highest_confidence` | `temporal` | `most_recent` | -| `02_Fraud_Detection` | `ClusterBuilder` (graph_based) | `merge_all` | `logical` | `expert_review` | -| **Biomedical** | -| `01_Drug_Discovery_Pipeline` | `EntityResolver` (semantic) | - | `relationship` | `voting` | -| `02_Genomic_Variant_Analysis` | `DuplicateDetector` (group) | `keep_most_complete` | `value` | `credibility_weighted` | -| **Cybersecurity** | -| `01_Real_Time_Anomaly_Detection` | `DuplicateDetector` (pairwise) | `keep_first` | `entity` | `first_seen` | -| `02_Threat_Intelligence_Hybrid_RAG` | `EntityResolver` (exact) | - | `type` | `highest_confidence` | -| **Blockchain** | -| `01_DeFi_Protocol_Intelligence` | `DuplicateDetector` (group) | `keep_last` | `relationship` | `voting` | -| `02_Transaction_Network_Analysis` | `ClusterBuilder` (hierarchical) | `keep_most_complete` | `temporal` | `most_recent` | -| **Intelligence** | -| `01_Criminal_Network_Analysis` | `EntityResolver` (fuzzy) | - | `value` | `credibility_weighted` | -| `02_Intelligence_Analysis_Orchestrator_Worker` | `DuplicateDetector` (batch) | `merge_all` | `entity` | `voting` | -| **Renewable Energy** | -| `01_Energy_Market_Analysis` | `DuplicateDetector` (pairwise) | `keep_highest_confidence` | `temporal` | `most_recent` | -| **Supply Chain** | -| `01_Supply_Chain_Data_Integration` | `DuplicateDetector` (incremental) | `keep_most_complete` | `value` | `credibility_weighted` | - ---- - -## Strategy Rationale by Domain - -### Finance -- **Financial Data Integration**: Incremental for streaming data; most_recent for time-sensitive financial data -- **Fraud Detection**: Graph-based clustering for fraud groups; expert_review for fraud assessment - -### Biomedical -- **Drug Discovery**: Semantic matching for drug compounds; voting for research source aggregation -- **Genomic Variants**: Group method for related variants; credibility weighting for research sources - -### Cybersecurity -- **Real-Time Anomaly**: Pairwise for real-time streams; keep_first for first detection priority -- **Threat Intelligence**: Exact matching for IOCs; highest_confidence for threat classification - -### Blockchain -- **DeFi Protocols**: Group method for related protocols; keep_last for latest protocol info -- **Transaction Networks**: Hierarchical clustering for nested groups; temporal for time-sensitive data - -### Intelligence -- **Criminal Networks**: Fuzzy matching for intelligence data; credibility weighting for intelligence sources -- **Intelligence Analysis**: Batch for multi-source integration; merge_all to combine all intelligence sources - -### Renewable Energy -- **Energy Markets**: Pairwise for real-time market data; most_recent for time-sensitive energy data - -### Supply Chain -- **Supply Chain Integration**: Incremental for continuous updates; credibility weighting for supply chain sources - ---- - -## Method Distribution - -### Deduplication Methods (9 total) -- `pairwise`: 2 notebooks (real-time processing) -- `batch`: 3 notebooks (large datasets) -- `incremental`: 2 notebooks (streaming/continuous) -- `group`: 2 notebooks (related entities) -- `graph_based` (ClusterBuilder): 2 notebooks (interconnected entities) -- `hierarchical` (ClusterBuilder): 1 notebook (nested groups) -- `exact` (EntityResolver): 1 notebook (exact matching) -- `semantic` (EntityResolver): 2 notebooks (semantic similarity) -- `fuzzy` (EntityResolver): 1 notebook (fuzzy matching) - -### Merge Strategies (5 total) -- `keep_first`: 1 notebook (first detection priority) -- `keep_last`: 1 notebook (latest information) -- `keep_most_complete`: 5 notebooks (preserve all details) -- `keep_highest_confidence`: 2 notebooks (most reliable data) -- `merge_all`: 3 notebooks (combine all information) - -### Conflict Detection Methods (6 total) -- `value`: 4 notebooks (property value conflicts) -- `type`: 2 notebooks (type/classification conflicts) -- `entity`: 2 notebooks (entity-wide conflicts) -- `relationship`: 3 notebooks (relationship conflicts) -- `temporal`: 3 notebooks (time-sensitive conflicts) -- `logical`: 2 notebooks (logical inconsistencies) - -### Conflict Resolution Strategies (6 total) -- `voting`: 5 notebooks (majority vote) -- `credibility_weighted`: 4 notebooks (source credibility) -- `most_recent`: 3 notebooks (latest data) -- `first_seen`: 1 notebook (first detection) -- `highest_confidence`: 2 notebooks (most confident) -- `expert_review`: 1 notebook (manual review) - ---- - -## Key Patterns - -1. **Real-Time Systems**: Use `pairwise` + `keep_first` + `first_seen` -2. **Time-Sensitive Data**: Use `temporal` + `most_recent` -3. **Multi-Source Integration**: Use `batch` + `merge_all` + `voting` -4. **Medical/Research**: Use `credibility_weighted` for authoritative sources -5. **Fraud/Security**: Use `graph_based` + `logical` + `expert_review` -6. **Exact Matching Required**: Use `exact` strategy (IOCs, identifiers) - diff --git a/semantica-explorer/.gitignore b/explorer/.gitignore similarity index 100% rename from semantica-explorer/.gitignore rename to explorer/.gitignore diff --git a/explorer/README.md b/explorer/README.md new file mode 100644 index 00000000..2bb6b2c4 --- /dev/null +++ b/explorer/README.md @@ -0,0 +1,197 @@ +# Semantica Knowledge Explorer + +A real-time visual interface for exploring knowledge graphs, decision intelligence, entity resolution, ontologies, and graph analytics built on top of the [Semantica](https://github.com/Hawksight-AI/semantica) library. + +--- + +## Requirements + +| Dependency | Minimum Version | +|---|---| +| Node.js | 18.x or higher (20.x recommended) | +| npm | 9.x or higher | +| Python | 3.8+ | +| Semantica backend | running on `http://127.0.0.1:8000` | + +Check your versions: + +```bash +node --version +npm --version +python --version +``` + +--- + +## Quick Start (Local Development) + +### 1. Clone the repository + +```bash +git clone https://github.com/Hawksight-AI/semantica.git +cd semantica +``` + +### 2. Install the Semantica Python package + +```bash +pip install semantica +``` + +Or install from source if you have the repo: + +```bash +pip install -e . +``` + +### 3. Start the Semantica backend + +The Explorer proxies all `/api` and `/ws` requests to `http://127.0.0.1:8000`. The backend must be running before you open the UI. + +```bash +# From the repo root +python -m semantica.server +``` + +The backend starts on port **8000** by default. Keep this terminal open. + +### 4. Install frontend dependencies + +Open a second terminal: + +```bash +cd explorer +npm install +``` + +> **Note:** This project uses Vite 5 and requires **Node 18+**. If you are on Node 16 or earlier, upgrade first. + +### 5. Start the dev server + +```bash +npm run dev +``` + +Vite starts on **http://localhost:5173** by default. Open that URL in your browser. + +--- + +## What you should see + +The Explorer opens with a persistent left sidebar and six workspace tabs: + +| Tab | What it shows | +|---|---| +| **Knowledge Graph** | Interactive Sigma.js canvas — nodes, edges, zoom, ForceAtlas2 layout | +| **Timeline** | Temporal event scrubber over the graph | +| **Decisions** | Causal chain viewer with outcome badges and decision filter | +| **Registry** | Live audit log of every graph mutation (add-node, add-edge, etc.) | +| **Entity Resolution** | Duplicate detection and entity merge workflow | +| **KG Overview** | Aggregate stats, community breakdown, centrality heatmap | +| **Ontology** | SKOS/OWL vocabulary hierarchy and schema summary | + +--- + +## Project structure + +``` +explorer/ +├── src/ +│ ├── App.tsx # Root layout, tab routing, workspace wiring +│ ├── index.css # Global resets, fonts, keyframe animations +│ ├── store/ +│ │ └── registryStore.ts # Pub/sub audit registry (no external state lib) +│ └── workspaces/ +│ ├── GraphWorkspace/ # Sigma.js graph canvas + inspector panel +│ ├── DecisionWorkspace/ # Causal flow diagram + decision list +│ ├── TimelineWorkspace/ # vis-timeline temporal scrubber +│ ├── ManageWorkspace/ # Registry, KG Overview, Ontology tabs +│ └── EnrichWorkspace/ # Entity resolution tab +├── index.html +├── vite.config.ts # Dev proxy → 127.0.0.1:8000, build → ../semantica/static +└── package.json +``` + +--- + +## Available scripts + +Run these from inside the `explorer/` directory: + +```bash +# Start the dev server with hot module replacement +npm run dev + +# Type-check and build a production bundle into ../semantica/static +npm run build + +# Preview the production build locally +npm run preview + +# Run ESLint over all source files +npm run lint + +# Run the graph store multi-edge unit tests +npm run test:graph-store +``` + +--- + +## API & WebSocket proxy + +During development, Vite forwards requests automatically — no CORS configuration needed: + +| Pattern | Forwarded to | +|---|---| +| `/api/*` | `http://127.0.0.1:8000/api/*` | +| `/ws` | `ws://127.0.0.1:8000/ws` | + +If you run the backend on a different port, update `server.proxy` in [vite.config.ts](vite.config.ts). + +--- + +## Production build + +```bash +cd explorer +npm run build +``` + +The compiled assets are written to `../semantica/static/`. The Semantica Python server serves this folder automatically at its root URL — no separate web server needed. + +--- + +## Troubleshooting + +**Blank graph / no data loads** +- Make sure the Semantica backend is running (`python -m semantica.server`) before opening the UI. +- Check the browser console for failed `/api/graph` requests — the proxy target may need updating in `vite.config.ts`. + +**`npm install` fails or hangs** +- Ensure you are using **Node 18 or 20**. Node 16 and Vite 5 are incompatible. +- Delete `node_modules/` and `package-lock.json`, then re-run `npm install`. + +**Port 5173 already in use** +- Vite will automatically try the next available port and print it in the terminal. Use that URL instead. + +**WebSocket not connecting (real-time mutations not appearing)** +- Confirm the backend exposes a `/ws` WebSocket endpoint. +- Check browser DevTools → Network → WS tab for the connection status. + +--- + +## Tech stack + +- **React 19** + TypeScript (strict `noUnusedLocals`) +- **Vite 5** with `babel-plugin-react-compiler` +- **Sigma.js 3** + **Graphology** — graph rendering and in-memory graph store +- **ForceAtlas2** — physics-based layout worker +- **@tanstack/react-query** — data fetching for ontology and vocab tabs +- **vis-timeline** — temporal event visualization +- **lucide-react** — icon set + +--- + +## Contributing + +See the root [CONTRIBUTING.md](../CONTRIBUTING.md) and open issues on the main [Semantica repository](https://github.com/Hawksight-AI/semantica). diff --git a/semantica-explorer/eslint.config.js b/explorer/eslint.config.js similarity index 100% rename from semantica-explorer/eslint.config.js rename to explorer/eslint.config.js diff --git a/semantica-explorer/index.html b/explorer/index.html similarity index 87% rename from semantica-explorer/index.html rename to explorer/index.html index 5ec53a70..b2f08fac 100644 --- a/semantica-explorer/index.html +++ b/explorer/index.html @@ -4,7 +4,7 @@ - semantica-explorer + Semantica Knowledge Explorer
diff --git a/semantica-explorer/package-lock.json b/explorer/package-lock.json similarity index 80% rename from semantica-explorer/package-lock.json rename to explorer/package-lock.json index 64e09c6f..1f057188 100644 --- a/semantica-explorer/package-lock.json +++ b/explorer/package-lock.json @@ -1,11 +1,11 @@ { - "name": "semantica-explorer", + "name": "semantica-knowledge-explorer", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "semantica-explorer", + "name": "semantica-knowledge-explorer", "version": "0.0.0", "dependencies": { "@monaco-editor/react": "^4.7.0", @@ -30,12 +30,11 @@ "devDependencies": { "@babel/core": "^7.29.0", "@eslint/js": "^9.39.4", - "@rolldown/plugin-babel": "^0.2.1", "@types/babel__core": "^7.20.5", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", + "@vitejs/plugin-react": "^4.3.0", "babel-plugin-react-compiler": "^1.0.0", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", @@ -44,7 +43,7 @@ "tsx": "^4.21.0", "typescript": "~5.9.3", "typescript-eslint": "^8.57.0", - "vite": "^8.0.1" + "vite": "^5.4.0" } }, "node_modules/@babel/code-frame": { @@ -179,6 +178,16 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -239,6 +248,38 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/runtime": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", @@ -309,43 +350,6 @@ "node": ">=0.8.0" } }, - "node_modules/@emnapi/core": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", - "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.7", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", @@ -946,29 +950,43 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1070,35 +1088,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, "node_modules/@react-dnd/asap": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", @@ -1117,10 +1106,31 @@ "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", "license": "MIT" }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", "cpu": [ "arm64" ], @@ -1129,15 +1139,12 @@ "optional": true, "os": [ "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", "cpu": [ "arm64" ], @@ -1146,15 +1153,12 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", "cpu": [ "x64" ], @@ -1163,15 +1167,26 @@ "optional": true, "os": [ "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", "cpu": [ "x64" ], @@ -1180,15 +1195,12 @@ "optional": true, "os": [ "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", "cpu": [ "arm" ], @@ -1197,15 +1209,26 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", "cpu": [ "arm64" ], @@ -1214,15 +1237,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", "cpu": [ "arm64" ], @@ -1231,15 +1251,40 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", "cpu": [ "ppc64" ], @@ -1248,15 +1293,54 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", "cpu": [ "s390x" ], @@ -1265,15 +1349,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", "cpu": [ "x64" ], @@ -1282,15 +1363,12 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", "cpu": [ "x64" ], @@ -1299,15 +1377,26 @@ "optional": true, "os": [ "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", "cpu": [ "arm64" ], @@ -1316,32 +1405,12 @@ "optional": true, "os": [ "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", "cpu": [ "arm64" ], @@ -1350,15 +1419,26 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", "cpu": [ "x64" ], @@ -1367,48 +1447,21 @@ "optional": true, "os": [ "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } + ] }, - "node_modules/@rolldown/plugin-babel": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@rolldown/plugin-babel/-/plugin-babel-0.2.2.tgz", - "integrity": "sha512-q9pE8+47bQNHb5eWVcE6oXppA+JTSwvnrhH53m0ZuHuK5MLvwsLoWrWzBTFQqQ06BVxz1gp0HblLsch8o6pvZw==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=22.12.0 || ^24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.29.0 || ^8.0.0-rc.1", - "@babel/plugin-transform-runtime": "^7.29.0 || ^8.0.0-rc.1", - "@babel/runtime": "^7.27.0 || ^8.0.0-rc.1", - "rolldown": "^1.0.0-rc.5", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@babel/plugin-transform-runtime": { - "optional": true - }, - "@babel/runtime": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", - "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", - "dev": true, - "license": "MIT" + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@sigma/edge-curve": { "version": "3.1.0", @@ -1429,9 +1482,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.95.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.95.2.tgz", - "integrity": "sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.2.tgz", + "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==", "license": "MIT", "funding": { "type": "github", @@ -1439,12 +1492,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.95.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.95.2.tgz", - "integrity": "sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.2.tgz", + "integrity": "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.95.2" + "@tanstack/query-core": "5.99.2" }, "funding": { "type": "github", @@ -1454,17 +1507,6 @@ "react": "^18 || ^19" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1581,9 +1623,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", - "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1619,20 +1661,20 @@ "peer": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1642,9 +1684,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.58.2", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -1658,16 +1700,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3" }, "engines": { @@ -1679,18 +1721,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", "debug": "^4.4.3" }, "engines": { @@ -1701,18 +1743,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1723,9 +1765,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", "dev": true, "license": "MIT", "engines": { @@ -1736,21 +1778,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1761,13 +1803,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", "dev": true, "license": "MIT", "engines": { @@ -1779,21 +1821,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1803,7 +1845,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -1830,13 +1872,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -1859,16 +1901,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1879,17 +1921,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.58.2", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1914,29 +1956,24 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", - "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.7" + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@xyflow/react": { @@ -2067,9 +2104,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.12", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.12.tgz", - "integrity": "sha512-qyq26DxfY4awP2gIRXhhLWfwzwI+N5Nxk6iQi8EFizIaWIjqicQTE4sLnZZVdeKPRcVNoJOkkpfzoIYuvCKaIQ==", + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2080,9 +2117,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "dev": true, "license": "MIT", "dependencies": { @@ -2091,9 +2128,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -2111,11 +2148,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -2135,9 +2172,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001781", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", - "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "version": "1.0.30001788", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", + "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", "dev": true, "funding": [ { @@ -2388,16 +2425,6 @@ "dev": true, "license": "MIT" }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, "node_modules/dnd-core": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.1.tgz", @@ -2429,9 +2456,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.328", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.328.tgz", - "integrity": "sha512-QNQ5l45DzYytThO21403XN3FvK0hOkWDG8viNf6jqS42msJ8I4tGDSpBCgvDRRPnkffafiwAym2X2eHeGD2V0w==", + "version": "1.5.340", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", + "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", "dev": true, "license": "ISC" }, @@ -2561,9 +2588,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -2577,7 +2604,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react-refresh": { @@ -2846,9 +2873,9 @@ } }, "node_modules/globals": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", "dev": true, "license": "MIT", "engines": { @@ -3157,267 +3184,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3464,9 +3230,9 @@ } }, "node_modules/lucide-react": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.7.0.tgz", - "integrity": "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.8.0.tgz", + "integrity": "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -3568,9 +3334,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "dev": true, "license": "MIT" }, @@ -3702,9 +3468,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -3772,9 +3538,9 @@ } }, "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3837,15 +3603,15 @@ } }, "node_modules/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.4" + "react": "^19.2.5" } }, "node_modules/react-dropzone": { @@ -3871,6 +3637,16 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-window": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz", @@ -3914,47 +3690,51 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@types/estree": "1.0.8" }, "bin": { - "rolldown": "bin/cli.mjs" + "rollup": "dist/bin/rollup" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" } }, - "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", - "dev": true, - "license": "MIT" - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -4047,14 +3827,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -4130,16 +3910,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4150,7 +3930,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/undici-types": { @@ -4278,23 +4058,21 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", - "tinyglobby": "^0.2.15" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -4303,35 +4081,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -4346,15 +4114,439 @@ }, "terser": { "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true } } }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/semantica-explorer/package.json b/explorer/package.json similarity index 91% rename from semantica-explorer/package.json rename to explorer/package.json index fb698fd5..23df2b07 100644 --- a/semantica-explorer/package.json +++ b/explorer/package.json @@ -1,5 +1,5 @@ { - "name": "semantica-explorer", + "name": "semantica-knowledge-explorer", "private": true, "version": "0.0.0", "type": "module", @@ -34,12 +34,11 @@ "devDependencies": { "@babel/core": "^7.29.0", "@eslint/js": "^9.39.4", - "@rolldown/plugin-babel": "^0.2.1", "@types/babel__core": "^7.20.5", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.1", + "@vitejs/plugin-react": "^4.3.0", "babel-plugin-react-compiler": "^1.0.0", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", @@ -48,6 +47,6 @@ "tsx": "^4.21.0", "typescript": "~5.9.3", "typescript-eslint": "^8.57.0", - "vite": "^8.0.1" + "vite": "^5.4.0" } } diff --git a/semantica-explorer/public/favicon.svg b/explorer/public/favicon.svg similarity index 100% rename from semantica-explorer/public/favicon.svg rename to explorer/public/favicon.svg diff --git a/semantica-explorer/public/icons.svg b/explorer/public/icons.svg similarity index 100% rename from semantica-explorer/public/icons.svg rename to explorer/public/icons.svg diff --git a/semantica-explorer/src/App.css b/explorer/src/App.css similarity index 100% rename from semantica-explorer/src/App.css rename to explorer/src/App.css diff --git a/semantica-explorer/src/App.tsx b/explorer/src/App.tsx similarity index 81% rename from semantica-explorer/src/App.tsx rename to explorer/src/App.tsx index 1e44b247..17d8bc78 100644 --- a/semantica-explorer/src/App.tsx +++ b/explorer/src/App.tsx @@ -10,11 +10,16 @@ const LineageDiagram = lazy(() => import('./workspaces/LineageWorkspace/LineageD const ReasoningWorkspace = lazy(() => import('./workspaces/ReasoningWorkspace').then((module) => ({ default: module.ReasoningWorkspace }))); const SparqlWorkspace = lazy(() => import('./workspaces/SparqlWorkspace/SparqlWorkspace').then((module) => ({ default: module.SparqlWorkspace }))); const VocabularyWorkspace = lazy(() => import('./workspaces/VocabularyWorkspace/VocabularyWorkspace').then((module) => ({ default: module.VocabularyWorkspace }))); +const RegistryTab = lazy(() => import('./workspaces/EnrichWorkspace/RegistryTab').then((module) => ({ default: module.RegistryTab }))); +const EntityResolutionTab = lazy(() => import('./workspaces/EnrichWorkspace/EntityResolutionTab').then((module) => ({ default: module.EntityResolutionTab }))); +const KGOverviewTab = lazy(() => import('./workspaces/ManageWorkspace/KGOverviewTab').then((module) => ({ default: module.KGOverviewTab }))); +const OntologySummaryTab = lazy(() => import('./workspaces/ManageWorkspace/OntologySummaryTab').then((module) => ({ default: module.OntologySummaryTab }))); type WorkspaceId = 'explore' | 'analyze' | 'decisions' | 'enrich' | 'manage'; type ExploreView = 'graph' | 'vocabulary'; type AnalyzeView = 'sparql' | 'reasoning'; -type EnrichView = 'import' | 'merge'; +type EnrichView = 'import' | 'merge' | 'registry' | 'resolve'; +type ManageView = 'lineage' | 'kg-overview' | 'ontology'; type NavItem = { id: WorkspaceId; @@ -26,7 +31,7 @@ type NavItem = { const queryClient = new QueryClient(); const navItems: NavItem[] = [ - { id: 'explore', label: 'Explore', hint: 'Graph and vocabulary browsing', icon: Database }, + { id: 'explore', label: 'Knowledge Explorer', hint: 'Graph and vocabulary browsing', icon: Database }, { id: 'analyze', label: 'Analyze', hint: 'Query and inspect the dataset', icon: FileSearch }, { id: 'decisions', label: 'Decisions', hint: 'Decision chains and precedent review', icon: Scale }, { id: 'enrich', label: 'Enrich', hint: 'Import, export, and merge workflows', icon: GitBranchPlus }, @@ -275,19 +280,21 @@ function WorkspaceShell({ subtitle, tabs, compact = false, + kicker = 'Workspace', children, }: { title: string; subtitle?: string; tabs?: ReactNode; compact?: boolean; + kicker?: string; children: ReactNode; }) { return (
-
Workspace
+
{kicker}

{title}

{subtitle ?
{subtitle}
: null} @@ -309,6 +316,7 @@ export default function App() { const [exploreView, setExploreView] = useState('graph'); const [analyzeView, setAnalyzeView] = useState('reasoning'); const [enrichView, setEnrichView] = useState('import'); + const [manageView, setManageView] = useState('lineage'); const renderWorkspace = () => { if (activeWorkspace === 'explore') { @@ -316,6 +324,7 @@ export default function App() { @@ -340,6 +349,7 @@ export default function App() { + + } > }> - {enrichView === 'import' ? : } + {enrichView === 'import' ? : + enrichView === 'merge' ? : + enrichView === 'resolve' ? : + } ); @@ -397,10 +418,29 @@ export default function App() { return ( + + + + + } > }> - + {manageView === 'lineage' ? : + manageView === 'kg-overview' ? : + { + setActiveWorkspace('explore'); + setExploreView('vocabulary'); + }} />} ); @@ -411,7 +451,7 @@ export default function App() {
+ )} +
+
+ + {/* Right Column — Decision Detail */} +
+ {/* Radial accent */} +
+ + {selectedDecision ? ( +
+ {/* Decision header */} +
+
+
+
+ Decision ID +
+

+ {selectedDecision.decision_id} +

+
+ {selectedDecision.outcome ? : null} +
+ + {selectedDecision.category ? ( +
+ {selectedDecision.category} +
+ ) : null} +
+ + {/* Causal Chain */} +
+
+
+

+ Causal Chain +

+ {chain.length > 0 && !loading ? ( + + {chain.length} step{chain.length !== 1 ? "s" : ""} + + ) : null} +
+ +
+
+ ) : ( +
+ Select a decision to inspect its causal chain. +
+ )} +
+
+ ); +} + +/* ─── styles ─────────────────────────────────────────────────────── */ + +const filterInputStyle: React.CSSProperties = { + width: "100%", + padding: "7px 10px 7px 30px", + background: "rgba(0,0,0,0.25)", + border: "1px solid rgba(88,166,255,0.16)", + borderRadius: 8, + color: "#c6d4e3", + fontSize: 12, + outline: "none", + boxSizing: "border-box", +}; diff --git a/semantica-explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx b/explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx similarity index 93% rename from semantica-explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx rename to explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx index 1d71c33c..e27e758f 100644 --- a/semantica-explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx +++ b/explorer/src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx @@ -2,6 +2,7 @@ * src/workspaces/DiffMergeWorkspace/DiffMergeWorkspace.tsx */ import { useState } from "react"; +import { logEvent } from "../../store/registryStore"; const THEME_CSS = ` .glass-panel { @@ -29,6 +30,11 @@ export function DiffMergeWorkspace() { const data = await res.json(); if (data.merged_into) { setMsg(`Merge success: redirected ${data.edges_updated} edges to ${data.merged_into}`); + logEvent("merge", `Merged ${duplicateId} → ${data.merged_into} · ${data.edges_updated} edges redirected`, { + primary: data.merged_into, + duplicate: duplicateId, + edgesUpdated: data.edges_updated, + }); } else { setMsg("Merge failed..."); } diff --git a/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx b/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx new file mode 100644 index 00000000..5d1c9151 --- /dev/null +++ b/explorer/src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx @@ -0,0 +1,450 @@ +/** + * src/workspaces/EnrichWorkspace/EntityResolutionTab.tsx + * + * Entity Resolution — run duplicate detection, review flagged pairs, + * perform one-click merges, and view merge history from the Registry. + */ +import { useState, useCallback } from "react"; +import { ScanSearch, GitMerge, X, ChevronDown, ChevronRight, Loader2 } from "lucide-react"; +import { logEvent, useRegistry } from "../../store/registryStore"; + +interface DedupPair { + a: { id: string; label: string; type: string }; + b: { id: string; label: string; type: string }; + score: number; + dismissed?: boolean; +} + +interface RawDuplicateItem { + entity_a?: string | Record; + entity_b?: string | Record; + similarity?: number; + score?: number; + [key: string]: unknown; +} + +function extractId(entity: string | Record | undefined): string { + if (!entity) return ""; + if (typeof entity === "string") return entity; + return String(entity.id ?? entity.text ?? JSON.stringify(entity)); +} + +function extractLabel(entity: string | Record | undefined): string { + if (!entity) return ""; + if (typeof entity === "string") return entity; + return String(entity.text ?? entity.label ?? entity.content ?? entity.id ?? ""); +} + +function extractType(entity: string | Record | undefined): string { + if (!entity || typeof entity === "string") return "entity"; + return String(entity.type ?? "entity"); +} + +function parseDuplicates(raw: RawDuplicateItem[]): DedupPair[] { + return raw.map((item) => ({ + a: { + id: extractId(item.entity_a as string | Record), + label: extractLabel(item.entity_a as string | Record), + type: extractType(item.entity_a as string | Record), + }, + b: { + id: extractId(item.entity_b as string | Record), + label: extractLabel(item.entity_b as string | Record), + type: extractType(item.entity_b as string | Record), + }, + score: Number(item.similarity ?? item.score ?? 0), + })); +} + +function ScoreBar({ score }: { score: number }) { + const pct = Math.min(100, Math.round(score * 100)); + const color = score >= 0.9 ? "#ff7b72" : score >= 0.75 ? "#f2b66d" : "#4cc38a"; + return ( +
+
+
+
+ + {pct}% + +
+ ); +} + +function PairRow({ + pair, + onMerge, + onDismiss, +}: { + pair: DedupPair; + onMerge: (primaryId: string, duplicateId: string) => Promise; + onDismiss: () => void; +}) { + const [expanded, setExpanded] = useState(false); + const [merging, setMerging] = useState(false); + + const handleMerge = async () => { + setMerging(true); + await onMerge(pair.a.id, pair.b.id); + setMerging(false); + }; + + return ( +
+
+ {/* Expand */} + + + {/* Entity Labels */} +
+
+ {pair.a.label || pair.a.id} + + {pair.b.label || pair.b.id} +
+
+ +
+
+ + {/* Actions */} +
+ + +
+
+ + {/* Expanded diff */} + {expanded ? ( +
+ {[ + { label: "Primary (keep)", entity: pair.a, accentColor: "#4aa3ff" }, + { label: "Duplicate (remove)", entity: pair.b, accentColor: "#ff7b72" }, + ].map(({ label, entity, accentColor }) => ( +
+
+ {label} +
+
{entity.label || entity.id}
+
{entity.type}
+
{entity.id}
+
+ ))} +
+ ) : null} +
+ ); +} + +export function EntityResolutionTab() { + const [threshold, setThreshold] = useState(0.82); + const [scanning, setScanning] = useState(false); + const [pairs, setPairs] = useState([]); + const [scanError, setScanError] = useState(""); + const registryEntries = useRegistry(); + + const mergeHistory = registryEntries.filter((e) => e.op === "merge"); + + const handleScan = useCallback(async () => { + setScanning(true); + setScanError(""); + try { + const res = await fetch("/api/enrich/dedup", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ threshold }), + }); + if (!res.ok) { + const err = await res.json().catch(() => ({})); + throw new Error((err as Record).detail ?? `Scan failed (${res.status})`); + } + const data = await res.json(); + const rawDuplicates: RawDuplicateItem[] = Array.isArray(data.duplicates) + ? (data.duplicates as RawDuplicateItem[]) + : []; + const parsed = parseDuplicates(rawDuplicates); + setPairs(parsed); + logEvent("import", `Dedup scan found ${parsed.length} flagged pair${parsed.length !== 1 ? "s" : ""} (threshold ${threshold.toFixed(2)})`, { + threshold, + flagged: parsed.length, + }); + } catch (err) { + setScanError(err instanceof Error ? err.message : "Scan failed"); + } finally { + setScanning(false); + } + }, [threshold]); + + const handleMerge = useCallback(async (primaryId: string, duplicateId: string) => { + try { + const res = await fetch("/api/enrich/merge", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ primary_id: primaryId, duplicate_ids: [duplicateId] }), + }); + if (!res.ok) throw new Error(`Merge failed (${res.status})`); + const data = await res.json(); + logEvent("merge", `Merged ${duplicateId} → ${primaryId} · ${data.edges_updated ?? 0} edges redirected`, { + primary: primaryId, + duplicate: duplicateId, + edgesUpdated: data.edges_updated, + }); + setPairs((prev) => prev.filter((p) => !(p.a.id === primaryId && p.b.id === duplicateId))); + } catch (err) { + console.error("[EntityResolution] merge failed", err); + } + }, []); + + const handleDismiss = useCallback((index: number) => { + setPairs((prev) => prev.filter((_, i) => i !== index)); + }, []); + + return ( +
+ {/* Header */} +
+
+ +
+
Entity Resolution
+
Detect and merge duplicate entities in the knowledge graph
+
+
+
+ + {/* Scan controls */} +
+
+
+
+ + {threshold.toFixed(2)} +
+ setThreshold(parseFloat(e.target.value))} + style={{ width: "100%", accentColor: "#f2b66d", cursor: "pointer" }} + /> +
+ More results (0.50) + Fewer, higher confidence (0.99) +
+
+ +
+ {scanError ? ( +
{scanError}
+ ) : null} +
+ +
+ {/* Flagged pairs */} +
+ {pairs.length > 0 ? ( + <> +
+
+ {pairs.length} flagged pair{pairs.length !== 1 ? "s" : ""} +
+ +
+ {pairs.map((pair, index) => ( + handleDismiss(index)} + /> + ))} + + ) : ( +
+ +
+ No flagged pairs +
+
+ Set a similarity threshold and run a dedup scan to detect potential duplicates. +
+
+ )} +
+ + {/* Merge history sidebar */} + {mergeHistory.length > 0 ? ( +
+
+ Merge History +
+
+ {mergeHistory.map((entry) => ( +
+ +
+
+ {entry.summary} +
+
+ {entry.timestamp.toLocaleTimeString()} +
+
+
+ ))} +
+
+ ) : null} +
+
+ ); +} + +/* ─── styles ─────────────────────────────────────────────────────── */ + +const shellStyle: React.CSSProperties = { + display: "flex", + flexDirection: "column", + width: "100%", + height: "100%", + background: "#0d1117", + overflow: "hidden", +}; + +const headerStyle: React.CSSProperties = { + padding: "20px 24px 16px", + borderBottom: "1px solid rgba(88,166,255,0.1)", + flexShrink: 0, +}; + +const controlsCardStyle: React.CSSProperties = { + margin: "16px 24px", + padding: "16px 20px", + borderRadius: 14, + background: "linear-gradient(135deg, rgba(13,17,23,0.75), rgba(22,27,34,0.6))", + border: "1px solid rgba(242,182,109,0.18)", + flexShrink: 0, +}; + +const scanBtnStyle: React.CSSProperties = { + display: "inline-flex", + alignItems: "center", + gap: 7, + padding: "10px 18px", + borderRadius: 10, + background: "linear-gradient(135deg, rgba(242,182,109,0.22), rgba(242,182,109,0.1))", + border: "1px solid rgba(242,182,109,0.32)", + color: "#f2b66d", + fontSize: 13, + fontWeight: 700, + cursor: "pointer", + flexShrink: 0, +}; + +const pairCardStyle: React.CSSProperties = { + padding: "12px 14px", + borderRadius: 12, + background: "linear-gradient(135deg, rgba(13,17,23,0.6), rgba(22,27,34,0.4))", + border: "1px solid rgba(255,255,255,0.07)", +}; + +const entityChipStyle: React.CSSProperties = { + display: "inline-block", + padding: "4px 10px", + borderRadius: 8, + background: "rgba(255,255,255,0.04)", + border: "1px solid rgba(255,255,255,0.08)", + color: "#e6edf3", + fontSize: 12, + fontWeight: 600, +}; + +const actionBtnStyle: React.CSSProperties = { + display: "inline-flex", + alignItems: "center", + gap: 5, + padding: "5px 10px", + borderRadius: 8, + fontSize: 11, + fontWeight: 700, + cursor: "pointer", +}; + +const iconBtnStyle: React.CSSProperties = { + background: "transparent", + border: "none", + color: "#8b949e", + cursor: "pointer", + padding: 4, + borderRadius: 6, + display: "flex", + alignItems: "center", +}; + +const diffCardStyle: React.CSSProperties = { + padding: "10px 12px", + borderRadius: 10, + background: "rgba(0,0,0,0.2)", + border: "1px solid transparent", +}; + +const historyPanelStyle: React.CSSProperties = { + width: 240, + borderLeft: "1px solid rgba(255,255,255,0.06)", + padding: "16px 16px", + overflowY: "auto", + flexShrink: 0, +}; + +const historyRowStyle: React.CSSProperties = { + display: "flex", + alignItems: "flex-start", + gap: 7, + padding: "8px 0", + borderBottom: "1px solid rgba(255,255,255,0.04)", +}; + +const emptyStateStyle: React.CSSProperties = { + flex: 1, + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + padding: 40, + minHeight: 200, +}; + +const clearAllBtnStyle: React.CSSProperties = { + background: "transparent", + border: "none", + color: "#8b949e", + fontSize: 12, + cursor: "pointer", + padding: "2px 6px", + borderRadius: 6, +}; diff --git a/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx b/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx new file mode 100644 index 00000000..e1178eaa --- /dev/null +++ b/explorer/src/workspaces/EnrichWorkspace/RegistryTab.tsx @@ -0,0 +1,284 @@ +/** + * src/workspaces/EnrichWorkspace/RegistryTab.tsx + * + * Document Registry — a live, filterable chronological audit log of every + * KG / Ontology mutation that occurred in this session. + */ +import { useState } from "react"; +import { ClipboardList, Filter, Trash2, ChevronDown, ChevronRight } from "lucide-react"; +import { useRegistry, clearRegistry, type RegistryEntryOp } from "../../store/registryStore"; + +const OP_META: Record< + RegistryEntryOp, + { label: string; color: string; bg: string; border: string } +> = { + import: { label: "IMPORT", color: "#4aa3ff", bg: "rgba(74,163,255,0.12)", border: "rgba(74,163,255,0.28)" }, + export: { label: "EXPORT", color: "#8fa8c6", bg: "rgba(143,168,198,0.08)", border: "rgba(143,168,198,0.18)" }, + merge: { label: "MERGE", color: "#f2b66d", bg: "rgba(242,182,109,0.12)", border: "rgba(242,182,109,0.28)" }, + "add-node": { label: "ADD NODE", color: "#4cc38a", bg: "rgba(76,195,138,0.12)", border: "rgba(76,195,138,0.28)" }, + "add-edge": { label: "ADD EDGE", color: "#4cc38a", bg: "rgba(76,195,138,0.10)", border: "rgba(76,195,138,0.22)" }, + delete: { label: "DELETE", color: "#ff7b72", bg: "rgba(255,123,114,0.12)", border: "rgba(255,123,114,0.28)" }, + infer: { label: "INFER", color: "#d2a8ff", bg: "rgba(210,168,255,0.12)", border: "rgba(210,168,255,0.28)" }, + "vocab-import": { label: "VOCAB", color: "#79c0ff", bg: "rgba(121,192,255,0.12)", border: "rgba(121,192,255,0.28)" }, +}; + +const ALL_OPS: (RegistryEntryOp | "all")[] = [ + "all", "import", "export", "merge", "add-node", "add-edge", "infer", "delete", "vocab-import", +]; + +function formatTimestamp(date: Date): string { + return date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit" }); +} + +function formatDate(date: Date): string { + return date.toLocaleDateString(undefined, { month: "short", day: "numeric" }); +} + +function EntryRow({ entry }: { entry: ReturnType[number] }) { + const [expanded, setExpanded] = useState(false); + const meta = OP_META[entry.op]; + const hasDetail = entry.detail && Object.keys(entry.detail).length > 0; + + return ( +
+
+ {/* Op Badge */} + + {meta.label} + + + {/* Content */} +
+
+ {entry.summary} +
+
+ {formatDate(entry.timestamp)} · {formatTimestamp(entry.timestamp)} +
+
+ + {/* Expand toggle */} + {hasDetail ? ( + + ) : null} +
+ + {/* Expanded detail */} + {expanded && hasDetail ? ( +
+          {JSON.stringify(entry.detail, null, 2)}
+        
+ ) : null} +
+ ); +} + +export function RegistryTab() { + const entries = useRegistry(); + const [activeFilter, setActiveFilter] = useState("all"); + + const filtered = activeFilter === "all" + ? entries + : entries.filter((e) => e.op === activeFilter); + + return ( +
+ {/* Header */} +
+
+ +
+
Document Registry
+
+ Audit log of all KG and Ontology mutations this session +
+
+
+
+ + {entries.length} event{entries.length !== 1 ? "s" : ""} + + {entries.length > 0 ? ( + + ) : null} +
+
+ + {/* Filter pills */} +
+ +
+ {ALL_OPS.map((op) => { + const isActive = op === activeFilter; + const meta = op === "all" ? null : OP_META[op as RegistryEntryOp]; + return ( + + ); + })} +
+
+ + {/* Feed */} +
+ {filtered.length === 0 ? ( +
+ +
+ No events recorded yet +
+
+ Import a file, run reasoning, or merge entities to see activity appear here. +
+
+ ) : ( + filtered.map((entry) => ) + )} +
+
+ ); +} + +/* ─── styles ─────────────────────────────────────────────────────── */ + +const shellStyle: React.CSSProperties = { + display: "flex", + flexDirection: "column", + width: "100%", + height: "100%", + background: "#0d1117", + overflow: "hidden", +}; + +const headerStyle: React.CSSProperties = { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + padding: "20px 24px 16px", + borderBottom: "1px solid rgba(88,166,255,0.1)", + flexShrink: 0, +}; + +const filterBarStyle: React.CSSProperties = { + display: "flex", + alignItems: "center", + gap: 10, + padding: "12px 24px", + borderBottom: "1px solid rgba(255,255,255,0.05)", + flexShrink: 0, +}; + +const feedStyle: React.CSSProperties = { + flex: 1, + overflowY: "auto", + padding: "16px 24px", + display: "flex", + flexDirection: "column", + gap: 8, +}; + +const entryCardStyle: React.CSSProperties = { + padding: "12px 14px", + borderRadius: 12, + background: "linear-gradient(135deg, rgba(13,17,23,0.6), rgba(22,27,34,0.4))", + border: "1px solid rgba(255,255,255,0.06)", + boxShadow: "inset 0 1px 0 rgba(255,255,255,0.03)", +}; + +const expandBtnStyle: React.CSSProperties = { + flexShrink: 0, + background: "transparent", + border: "none", + color: "#8b949e", + cursor: "pointer", + padding: 4, + borderRadius: 6, + display: "flex", + alignItems: "center", +}; + +const detailPreStyle: React.CSSProperties = { + marginTop: 10, + padding: "10px 12px", + borderRadius: 8, + background: "rgba(0,0,0,0.28)", + border: "1px solid rgba(255,255,255,0.06)", + color: "#79c0ff", + fontSize: 11, + fontFamily: "'JetBrains Mono', monospace", + overflowX: "auto", + whiteSpace: "pre-wrap", + wordBreak: "break-all", +}; + +const clearBtnStyle: React.CSSProperties = { + display: "inline-flex", + alignItems: "center", + gap: 5, + padding: "5px 10px", + borderRadius: 8, + border: "1px solid rgba(255,123,114,0.22)", + background: "rgba(255,123,114,0.06)", + color: "#ff7b72", + fontSize: 12, + fontWeight: 600, + cursor: "pointer", +}; + +const emptyStateStyle: React.CSSProperties = { + flex: 1, + display: "flex", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + padding: 40, + minHeight: 280, +}; diff --git a/semantica-explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx b/explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx similarity index 99% rename from semantica-explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx rename to explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx index ab3092d8..216b4142 100644 --- a/semantica-explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx +++ b/explorer/src/workspaces/GraphWorkspace/GraphCanvas.tsx @@ -116,13 +116,15 @@ const SIGMA_SETTINGS = { labelRenderedSizeThreshold: 6, defaultNodeType: "circle", defaultEdgeType: "line", - hideLabelsOnMove: true, - hideEdgesOnMove: true, + hideLabelsOnMove: false, + hideEdgesOnMove: false, enableEdgeEvents: true, renderEdgeLabels: false, labelDensity: 0.7, labelGridCellSize: 140, zIndex: true, + minCameraRatio: 0.04, + maxCameraRatio: 8, webGLTarget: "webgl2" as const, nodeProgramClasses: SEMANTICA_NODE_PROGRAM_CLASSES, edgeProgramClasses: SEMANTICA_EDGE_PROGRAM_CLASSES, diff --git a/semantica-explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx b/explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx similarity index 50% rename from semantica-explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx rename to explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx index c7150ac4..df30b0e5 100644 --- a/semantica-explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx +++ b/explorer/src/workspaces/GraphWorkspace/GraphInspectorPanel.tsx @@ -1,5 +1,5 @@ import type { CSSProperties } from "react"; - +import { Loader2 } from "lucide-react"; import { graph } from "../../store/graphStore"; import { GRAPH_THEME } from "./graphTheme"; @@ -14,6 +14,8 @@ export type PathResponse = { path: string[]; edge_ids?: string[]; total_weight: number; + hop_count: number; + distance_band: "direct" | "near" | "mid-range" | "distant"; }; export interface GraphInspectorPanelProps { @@ -22,11 +24,13 @@ export interface GraphInspectorPanelProps { predictionType: string; onPredictionTypeChange: (value: string) => void; onRunPredictions: () => void; + isRunningPredictions?: boolean; pathTargetId: string; onPathTargetChange: (value: string) => void; onTracePath: () => void; pathResult: PathResponse | null; onDownloadProvenance: (format: "json" | "markdown") => void; + onFocusNode?: (nodeId: string) => void; } const PROVENANCE_KEYS = ["source", "source_url", "pmid", "pmids", "evidence", "provenance", "confidence"] as const; @@ -37,22 +41,132 @@ function sourceAttribution(properties: Record) { .map((key) => ({ key, value: properties[key] })); } +/* ─── Path Flow Visualizer ──────────────────────────────────────── */ + +function getNodeLabel(nodeId: string): string { + if (!graph.hasNode(nodeId)) return nodeId; + const attrs = graph.getNodeAttributes(nodeId) as { label?: string; content?: string }; + return String(attrs.label ?? attrs.content ?? nodeId); +} + +function getEdgeLabelBetween(sourceId: string, targetId: string, edgeIds?: string[]): string { + // Try to find the specific edge from edgeIds first + if (edgeIds) { + for (const edgeId of edgeIds) { + if (graph.hasEdge(edgeId)) { + const [src, tgt] = graph.extremities(edgeId); + if ((src === sourceId && tgt === targetId) || (src === targetId && tgt === sourceId)) { + const attrs = graph.getEdgeAttributes(edgeId) as { edgeType?: string }; + return attrs.edgeType ?? "→"; + } + } + } + } + // Fallback: find any edge between the pair + if (graph.hasNode(sourceId) && graph.hasNode(targetId)) { + let label = "→"; + graph.forEachEdge(sourceId, targetId, (_edgeId, attrs) => { + const edgeAttrs = attrs as { edgeType?: string }; + if (edgeAttrs.edgeType) label = edgeAttrs.edgeType; + }); + return label; + } + return "→"; +} + +function PathFlowViz({ + path, + edgeIds, + totalWeight, + onFocusNode, +}: { + path: string[]; + edgeIds?: string[]; + totalWeight: number; + onFocusNode?: (nodeId: string) => void; +}) { + if (path.length === 0) { + return
No path found between the selected nodes.
; + } + + return ( +
+ {/* Horizontal scrollable chip flow */} +
+ {path.map((nodeId, index) => { + const label = getNodeLabel(nodeId); + const edgeLabel = + index < path.length - 1 + ? getEdgeLabelBetween(nodeId, path[index + 1], edgeIds) + : null; + + return ( +
+ {/* Node chip */} + + + {/* Edge connector */} + {edgeLabel !== null ? ( +
+
+ {edgeLabel} +
+
+
+
+
+ ) : null} +
+ ); + })} +
+ + {/* Weight badge */} +
+ Total weight: + {totalWeight.toFixed(3)} + · + {path.length} hops +
+
+ ); +} + +/* ─── Main Panel ─────────────────────────────────────────────────── */ + export function GraphInspectorPanel({ nodeId, predictions, predictionType, onPredictionTypeChange, onRunPredictions, + isRunningPredictions = false, pathTargetId, onPathTargetChange, onTracePath, pathResult, onDownloadProvenance, + onFocusNode, }: GraphInspectorPanelProps) { if (!nodeId) { return ( -
-

+

+
+
+
+

Search for a node or click one in the canvas to inspect its properties.

@@ -73,41 +187,21 @@ export function GraphInspectorPanel({ const accentColor = attributes?.color || "#58a6ff"; const propertyEntries = Object.entries(properties).filter( ([key]) => - ![ - "x", - "y", - "valid_from", - "valid_until", - "content", - "source", - "source_url", - "pmid", - "pmids", - "evidence", - "provenance", - "confidence", - ].includes(key), + !["x","y","valid_from","valid_until","content","source","source_url","pmid","pmids","evidence","provenance","confidence"].includes(key), ); return (