diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index fc0affbd..05f9e3af 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -23,3 +23,17 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Test with pytest
run: uv run --with pytest --with pytest-cov pytest --doctest-modules --cov=copilotj --cov-report=xml --cov-report=html --ignore=examples --pyargs copilotj
+
+ java-test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - name: Set up JDK 21
+ uses: actions/setup-java@v5
+ with:
+ distribution: zulu
+ java-version: "21"
+ cache: maven
+ - name: Run Java unit tests
+ working-directory: plugin
+ run: mvn -B test
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index 43537973..eff0da73 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -23,15 +23,16 @@ Run `just` or `just --list` to see all available commands.
You can also review the `justfile` to understand how each task is defined and configured, or refer to it directly if you prefer not to install [just](https://github.com/casey/just).
-| Command | Description |
-| ------------------- | ------------------------------ |
-| `just dev-server` | Run the bridge server |
-| `just dev-plugin` | Run the ImageJ plugin (debug) |
-| `just dev-web` | Run the web frontend |
-| `just test` | Run Python tests |
-| `just test-cov` | Run tests with coverage report |
-| `just build-plugin` | Build the plugin JAR |
-| `just build-web` | Build the web frontend |
+| Command | Description |
+| ------------------- | ----------------------------- |
+| `just dev-server` | Run the bridge server |
+| `just dev-plugin` | Run the ImageJ plugin (debug) |
+| `just dev-web` | Run the web frontend |
+| `just test` | Run tests |
+| `just test-python` | Run Python tests only |
+| `just test-plugin` | Run ImageJ plugin tests only |
+| `just build-plugin` | Build the plugin JAR |
+| `just build-web` | Build the web frontend |
## Running the components
@@ -278,8 +279,9 @@ and register any custom tools.
## Testing
```bash
-just test # Run Python tests
-just test-cov # Run tests with coverage report (HTML + XML)
+just test # Run tests
+just test-python # Run Python tests only
+just test-plugin # Run ImageJ plugin tests only
```
## Observability (optional)
diff --git a/copilotj/multiagent/leader_multiagent.py b/copilotj/multiagent/leader_multiagent.py
index ec51b94e..95d35f1d 100644
--- a/copilotj/multiagent/leader_multiagent.py
+++ b/copilotj/multiagent/leader_multiagent.py
@@ -784,6 +784,7 @@ async def run(self, task: str, trace_ctx: Langfuse | None = None) -> None:
"Reply with a single Thought + Action, or a Final Answer.",
trace_ctx=trace_ctx,
)
+ syntax_error_counter = 0 # Reset syntax error counter on success
except ModelSyntaxError as e:
syntax_error_counter += 1
self.log_error(
diff --git a/copilotj/plugin/awt/window/ij_image.py b/copilotj/plugin/awt/window/ij_image.py
index 861a81f3..af8c5300 100644
--- a/copilotj/plugin/awt/window/ij_image.py
+++ b/copilotj/plugin/awt/window/ij_image.py
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0
+import re
from typing import Literal, override
from copilotj.plugin._base import FromTo, Response, Verbosity
@@ -46,11 +47,42 @@ def _describe(self, *, level: int, verbosity: Verbosity) -> list[str]:
return [self._describe_one_line(level=level, verbosity=verbosity)]
+# Values of these metadata keys duplicate the structured fields rendered by IjImage._describe
+# (width/height/depth/channels/frames, calibration, units, bit depth). They are skipped on the
+# perception/display path only — the stored `info` keeps the full raw metadata. Note that `info`
+# also participates in IjImage.equals and is rendered verbatim on the diff path
+# (IjImageDifference via FromTo), so volatile metadata can still surface as snapshot diffs; that
+# tradeoff is intentional (see the matching note on the Java side, IjImage.buildImageMetadata).
+_INFO_REDUNDANT_KEYS = frozenset(
+ {
+ "SizeX",
+ "SizeY",
+ "SizeZ",
+ "SizeC",
+ "SizeT",
+ "PhysicalSizeX",
+ "PhysicalSizeY",
+ "PhysicalSizeZ",
+ "PhysicalSizeXUnit",
+ "PhysicalSizeYUnit",
+ "PhysicalSizeZUnit",
+ "BitsPerSample",
+ "BitsPerPixel",
+ "SignificantBits",
+ "PixelType",
+ }
+)
+# Matches Bio-Formats/TIFF-style "Key: Value" lines; non-matching lines (prose, OME-XML, "key=value"
+# headers from FileInfo.description) are left intact.
+_INFO_KV_RE = re.compile(r"^([A-Za-z0-9_]+)\s*:\s*(.+)$")
+
+
class IjImage(AwtWindowBase[TYPE]):
title: str
image_type: str
size: str
path: str | None
+ info: str | None = None
bit_depth: int
stack_size: int
@@ -121,6 +153,17 @@ def _describe(self, *, level: int, verbosity: Verbosity) -> list[str]:
if self.roi:
lines.append(self.roi._describe_one_line(level=level, verbosity=verbosity))
+ if self.info:
+ kept = []
+ for ln in self.info.splitlines():
+ m = _INFO_KV_RE.match(ln.strip())
+ if m and m.group(1) in _INFO_REDUNDANT_KEYS:
+ continue # already shown via the structured fields above
+ kept.append(ln)
+ if kept: # don't emit an empty "Metadata:" block when every line was redundant
+ lines.append("Metadata:")
+ lines.extend(kept)
+
return lines
@@ -129,6 +172,7 @@ class IjImageDifference(AwtWindowDifferenceBase[TYPE]):
image_type: FromTo[str] | None = None
size: FromTo[str] | None = None
path: FromTo[str | None] | None = None
+ info: FromTo[str | None] | None = None
bit_depth: FromTo[int] | None = None
stack_size: FromTo[int] | None = None
diff --git a/copilotj/test/multiagent/test_leader_syntax_reset.py b/copilotj/test/multiagent/test_leader_syntax_reset.py
new file mode 100644
index 00000000..988e51b2
--- /dev/null
+++ b/copilotj/test/multiagent/test_leader_syntax_reset.py
@@ -0,0 +1,155 @@
+# SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+#
+# SPDX-License-Identifier: Apache-2.0
+
+"""Tests for LeaderDriven.run() syntax-error counter reset.
+
+`syntax_error_counter` must track CONSECUTIVE ReAct syntax errors, not
+cumulative ones: a model that recovers (produces a valid response) between
+errors must have its counter reset, mirroring `tool_retry_counter` at L850.
+
+Control-flow fact these tests rely on (verified by trace + outside-voice
+review): every ModelSyntaxError increment in run() leaves `agent_resp = None`,
+which routes recovery through the single `send_correction` at ~L782. That site
+is the only place a reset is load-bearing.
+"""
+
+import asyncio
+
+from copilotj.core import ModelSyntaxError
+from copilotj.multiagent.leader_multiagent import LeaderDriven
+
+_MAX_SYNTAX_ERRORS = 3
+
+
+class _Args:
+ def model_dump(self) -> dict:
+ return {}
+
+
+class _Tool:
+ def __init__(self, name: str) -> None:
+ self.name = name
+
+
+class _ToolCall:
+ def __init__(self, name: str) -> None:
+ self.tool = _Tool(name)
+ self.args = _Args()
+
+
+class _Resp:
+ """Minimal stand-in for ModelResponse exercised by run()."""
+
+ def __init__(self, *, content=None, reasoning_content=None, tool_calls=None) -> None:
+ self.content = content
+ self.reasoning_content = reasoning_content
+ self.tool_calls = tool_calls
+
+
+def _syntax_err() -> ModelSyntaxError:
+ return ModelSyntaxError("bad ReAct")
+
+
+def _action() -> _Resp:
+ """A valid parsed response that carries a tool call (no final answer)."""
+ return _Resp(reasoning_content="thought", tool_calls=[_ToolCall("some_tool")])
+
+
+def _final() -> _Resp:
+ return _Resp(content="final answer")
+
+
+class _ScriptedLeader:
+ """LeaderAgent stub whose dialog methods pop a scripted queue.
+
+ Each queue holds either a _Resp (returned) or a ModelSyntaxError (raised).
+ """
+
+ def __init__(self) -> None:
+ self.begin_dialog_q: list = []
+ self.send_correction_q: list = []
+ self.continue_dialog_q: list = []
+ self.call_tool_q: list = []
+
+ @staticmethod
+ def _pop(queue: list):
+ item = queue.pop(0)
+ if isinstance(item, Exception):
+ raise item
+ return item
+
+ async def begin_dialog(self, task, trace_ctx=None):
+ return self._pop(self.begin_dialog_q)
+
+ async def send_correction(self, message, trace_ctx=None):
+ return self._pop(self.send_correction_q)
+
+ async def continue_dialog(self, prior_response, observation, trace_ctx=None):
+ return self._pop(self.continue_dialog_q)
+
+ async def _call_tool(self, tool_call):
+ return self._pop(self.call_tool_q)
+
+
+async def _noop_dialog_changed(*args, **kwargs) -> None:
+ pass
+
+
+async def _drive(leader: _ScriptedLeader) -> str:
+ """Run one LeaderDriven.run() dialog and return its final status.
+
+ Bypasses LeaderDriven.__init__ (which needs cfg/apis/model_client); run()
+ only touches the attributes wired up here.
+ """
+ pattern = LeaderDriven.__new__(LeaderDriven)
+ pattern.dialog_counter = 1
+ pattern._summarize_task = None # type: ignore[attr-defined]
+ pattern.log_info = lambda *a, **k: None # type: ignore[assignment]
+ pattern.log_error = lambda *a, **k: None # type: ignore[assignment]
+ pattern.dialog_changed = _noop_dialog_changed # type: ignore[assignment]
+ pattern.leader_agent = leader # type: ignore[assignment]
+
+ captured: dict = {}
+
+ async def _record(*args) -> None:
+ # _background_summarize_and_store(dialog_id, task, dialog_context)
+ captured["ctx"] = args[-1]
+
+ pattern._background_summarize_and_store = _record # type: ignore[assignment]
+
+ await pattern.run("do the thing")
+ await pattern._summarize_task # type: ignore[attr-defined]
+ return captured["ctx"]["status"]
+
+
+def test_cumulative_errors_with_recovery_complete():
+ """>=3 total syntax errors, each recovered from, must NOT abort.
+
+ Under the old cumulative counting this sequence aborted at the third error
+ (status "failed"); with the reset-on-recovery it completes.
+ """
+ leader = _ScriptedLeader()
+ leader.begin_dialog_q = [_syntax_err()]
+ # Three recoveries at site (a), each producing a valid tool call:
+ leader.send_correction_q = [_action(), _action(), _action()]
+ leader.call_tool_q = ["ok", "ok", "ok"]
+ # Two errors interleaved, then a final answer on the third continue:
+ leader.continue_dialog_q = [_syntax_err(), _syntax_err(), _final()]
+
+ status = asyncio.run(_drive(leader))
+
+ assert status == "completed"
+
+
+def test_consecutive_errors_still_abort():
+ """Three consecutive syntax errors (no recovery) must still abort."""
+ leader = _ScriptedLeader()
+ leader.begin_dialog_q = [_syntax_err()]
+ leader.send_correction_q = [_syntax_err(), _syntax_err()]
+
+ status = asyncio.run(_drive(leader))
+
+ assert status == "failed"
+ # Sanity: we exercised the abort threshold, not some other failure path.
+ assert len(leader.send_correction_q) == 0
diff --git a/flake.nix b/flake.nix
index 2404d9c5..90a10901 100644
--- a/flake.nix
+++ b/flake.nix
@@ -153,7 +153,6 @@
UV_PYTHON = python.interpreter;
# JDK configuration
JAVA_HOME = pkgs.jdk21.home;
- JAVA8_HOME = pkgs.jdk8.home;
}
// lib.optionalAttrs pkgs.stdenv.isLinux {
# Python libraries often load native shared objects using dlopen(3).
@@ -206,7 +205,6 @@
# JDK configuration
JAVA_HOME = pkgs.jdk21.home;
- JAVA8_HOME = pkgs.jdk8.home;
};
shellHook = ''
diff --git a/justfile b/justfile
index 1c5529f0..c1d9f1cf 100644
--- a/justfile
+++ b/justfile
@@ -2,7 +2,6 @@
#
# JDK configuration (set via flake.nix env vars):
# JAVA_HOME -> JDK 21 (builds the single JAR: core -> Java 8 bytecode, MCP -> Java 17)
-# JAVA8_HOME -> JDK 8 (only for ad-hoc Java 8 downgrade checks; not required to build)
default:
@just --list
@@ -23,6 +22,15 @@ build-plugin:
clean-plugin:
cd plugin && mvn clean
+# Test: run the Java unit tests (surefire). No Fiji/ImageJ runtime needed.
+test-plugin:
+ cd plugin && mvn test
+
+# Test + coverage: JaCoCo prepare-agent is already bound by pom-scijava-base,
+# so `mvn test` produces jacoco.exec; invoke report explicitly for the HTML.
+test-cov-plugin:
+ cd plugin && mvn test jacoco:report
+
copy-plugin-deps:
cd plugin && mvn dependency:copy-dependencies -DoutputDirectory=target/deps
@@ -35,13 +43,15 @@ build-web:
run-workflow:
scripts/run-workflow.sh
-test:
+test: test-python test-plugin
+
+test-python:
uv run --with pytest \
pytest \
--doctest-modules --ignore=examples \
--pyargs copilotj
-test-cov:
+test-pythn-cov:
uv run --with pytest --with pytest-cov \
pytest \
--doctest-modules --ignore=examples \
diff --git a/plugin/pom.xml b/plugin/pom.xml
index d5387872..dfccfdbc 100644
--- a/plugin/pom.xml
+++ b/plugin/pom.xml
@@ -5,7 +5,7 @@
org.scijava
pom-scijava
- 38.0.1
+ 40.0.0
@@ -248,6 +248,18 @@
ij1-patcher
provided
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit-jupiter.version}
+ test
+
diff --git a/plugin/src/main/java/copilotj/awt/Action.java b/plugin/src/main/java/copilotj/awt/Action.java
index 5dddffb8..c9dd047d 100644
--- a/plugin/src/main/java/copilotj/awt/Action.java
+++ b/plugin/src/main/java/copilotj/awt/Action.java
@@ -111,7 +111,7 @@ protected ParameterSchema(final String type, final String name, final String des
* Validates the given value against this parameter's schema.
*
* @param value The value to validate.
- * @return A list of error messages. An empty list indicates the value is valid.
+ * @return A list of error messages, or null if the value is valid.
*/
public abstract List validate(Object value);
}
@@ -146,12 +146,13 @@ public StringParameterSchema(final String name, final String description, final
// but for now, we'll assume it's a valid regex string if provided.
this.pattern = pattern;
- if (enumValues != null && enumValues.isEmpty()) {
- // Ensure no nulls or empty strings in enumValues
+ if (enumValues != null) {
+ // Reject null entries. Blank strings are legitimate widget labels (e.g. an
+ // AWT Choice/List placeholder item passed via ChoiceNode/ListNode.getActions()),
+ // so they must NOT be rejected here.
for (final String value : enumValues) {
- if (value == null || value.isEmpty()) {
- throw new IllegalArgumentException(
- "Enum values cannot contain null or empty strings for parameter: " + name);
+ if (value == null) {
+ throw new IllegalArgumentException("Enum values cannot contain null for parameter: " + name);
}
}
}
@@ -281,7 +282,7 @@ public List validate(final Object value) {
errors.add("Invalid type. Expected boolean, got " + value.getClass().getSimpleName());
}
}
- return errors;
+ return errors.isEmpty() ? null : errors;
}
}
diff --git a/plugin/src/main/java/copilotj/awt/window/IjImage.java b/plugin/src/main/java/copilotj/awt/window/IjImage.java
index 4077b69e..3ac2fe6f 100644
--- a/plugin/src/main/java/copilotj/awt/window/IjImage.java
+++ b/plugin/src/main/java/copilotj/awt/window/IjImage.java
@@ -10,8 +10,10 @@
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Properties;
import ij.ImagePlus;
import ij.gui.ImageWindow;
@@ -46,6 +48,7 @@ public static class Difference extends AbstractAwtWindow.Difference {
public final FromTo imageType;
public final FromTo size;
public final FromTo path;
+ public final FromTo info;
public final FromTo bitDepth;
public final FromTo stackSize;
@@ -81,6 +84,7 @@ public Difference(final IjImage from, final IjImage to, final boolean imageUpdat
this.imageType = !Objects.equals(from.imageType, to.imageType) ? new FromTo<>(from.imageType, to.imageType) : null;
this.size = !Objects.equals(from.size, to.size) ? new FromTo<>(from.size, to.size) : null;
this.path = !Objects.equals(from.path, to.path) ? new FromTo<>(from.path, to.path) : null;
+ this.info = !Objects.equals(from.info, to.info) ? new FromTo<>(from.info, to.info) : null;
this.bitDepth = !Objects.equals(from.bitDepth, to.bitDepth) ? new FromTo<>(from.bitDepth, to.bitDepth) : null;
this.stackSize = !Objects.equals(from.stackSize, to.stackSize) ? new FromTo<>(from.stackSize, to.stackSize) : null;
@@ -277,6 +281,7 @@ public static ImagePreviewWithInfo captureImage(final WindowIdentifier identifie
public final String imageType;
public final String size;
public final String path;
+ public final String info;
public final int bitDepth;
public final int stackSize;
@@ -328,6 +333,7 @@ public IjImage(final WindowIdentifier identifier, final ImageWindow w) {
}
}
this.path = path;
+ this.info = buildImageMetadata(imp);
this.bitDepth = imp.getBitDepth();
this.stackSize = imp.getStackSize();
@@ -362,12 +368,60 @@ public IjImage(final WindowIdentifier identifier, final ImageWindow w) {
}
}
+ /** Maximum characters captured for the image metadata block. Bounds leader context and snapshot
+ * diff churn from volatile or large metadata (e.g. OME-XML stored in the "Info" property). */
+ private static final int INFO_MAX_CHARS = 4000;
+ private static final String INFO_TRUNCATION_MARKER = "\n... [truncated, full metadata omitted]";
+
+ /** Assemble a readable, multi-line metadata block from the image's "Info" property, every other
+ * String-valued property, and the FileInfo description. Sources are deduped by exact whole-string
+ * equality (Info first); returns null when no metadata is present so the field stays absent. */
+ private static String buildImageMetadata(final ImagePlus imp) {
+ final LinkedHashSet blocks = new LinkedHashSet<>();
+
+ // 1. The "Info" property — full text, no line filtering.
+ final String infoProp = (String) imp.getProperty("Info");
+ if (infoProp != null && !infoProp.trim().isEmpty()) {
+ blocks.add(infoProp.trim());
+ }
+
+ // 2. Every other String-valued property (skip "Info", already captured; "HideInfo" is a UI flag).
+ final Properties props = imp.getProperties();
+ if (props != null) {
+ for (final String key : props.stringPropertyNames()) {
+ if ("Info".equals(key) || "HideInfo".equals(key)) {
+ continue;
+ }
+ final String value = props.getProperty(key);
+ if (value != null && !value.isEmpty()) {
+ blocks.add(key + ": " + value);
+ }
+ }
+ }
+
+ // 3. FileInfo.description — the raw file header; independent of the "Info" property.
+ final FileInfo fi = imp.getOriginalFileInfo();
+ if (fi != null && fi.description != null && !fi.description.trim().isEmpty()) {
+ blocks.add(fi.description.trim());
+ }
+
+ if (blocks.isEmpty()) {
+ return null;
+ }
+ String result = String.join("\n", blocks);
+ if (result.length() > INFO_MAX_CHARS) {
+ result = result.substring(0, INFO_MAX_CHARS) + INFO_TRUNCATION_MARKER;
+ }
+ return result;
+ }
+
public boolean equals(final IjImage other) {
return this.id == other.id
&& Objects.equals(this.title, other.title)
&& Objects.equals(this.type, other.type)
&& Objects.equals(this.size, other.size)
&& Objects.equals(this.path, other.path)
+ && Objects.equals(this.info, other.info)
&& this.bitDepth == other.bitDepth
&& this.stackSize == other.stackSize
diff --git a/plugin/src/main/java/copilotj/util/Trie.java b/plugin/src/main/java/copilotj/util/Trie.java
index 08074568..b5618105 100644
--- a/plugin/src/main/java/copilotj/util/Trie.java
+++ b/plugin/src/main/java/copilotj/util/Trie.java
@@ -261,7 +261,6 @@ private SimpleMap nodeToSimpleMap(final Node node, final KeyCombiner
childNode = onlyChild.getValue();
}
}
- System.out.println(word);
map.put(word, nodeToSimpleMap(childNode, combiner));
}
return map;
diff --git a/plugin/src/test/java/copilotj/BoundedLogTest.java b/plugin/src/test/java/copilotj/BoundedLogTest.java
new file mode 100644
index 00000000..b789fde0
--- /dev/null
+++ b/plugin/src/test/java/copilotj/BoundedLogTest.java
@@ -0,0 +1,92 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link BoundedLog}, a synchronized append-only log with a soft
+ * char cap. Lives in package {@code copilotj} so it can reference the
+ * package-private {@link BoundedLog#MAX_CHARS} directly.
+ */
+public class BoundedLogTest {
+
+ @Test
+ public void snapshotEmptyAfterConstruction() {
+ assertEquals("", new BoundedLog().snapshot());
+ }
+
+ @Test
+ public void appendIgnoresNull() {
+ final BoundedLog log = new BoundedLog();
+ log.append(null);
+ assertEquals("", log.snapshot());
+ }
+
+ @Test
+ public void appendIgnoresEmpty() {
+ final BoundedLog log = new BoundedLog();
+ log.append("");
+ assertEquals("", log.snapshot());
+ }
+
+ @Test
+ public void appendAndSnapshotJoinsInOrder() {
+ final BoundedLog log = new BoundedLog();
+ log.append("a");
+ log.append("b");
+ log.append("c");
+ assertEquals("abc", log.snapshot());
+ }
+
+ @Test
+ public void clearEmptiesLog() {
+ final BoundedLog log = new BoundedLog();
+ log.append("x");
+ log.clear();
+ assertEquals("", log.snapshot());
+ }
+
+ @Test
+ public void evictsOldestChunksOverCap() {
+ // Four MAX_CHARS/4 chunks fill exactly to the cap; a fifth evicts the oldest.
+ final BoundedLog log = new BoundedLog();
+ final int chunk = BoundedLog.MAX_CHARS / 4;
+ log.append(repeat('1', chunk));
+ log.append(repeat('2', chunk));
+ log.append(repeat('3', chunk));
+ log.append(repeat('4', chunk));
+ final String newest = repeat('5', chunk);
+ log.append(newest);
+
+ final String snap = log.snapshot();
+ assertTrue(snap.endsWith(newest), "newest chunk must be fully retained");
+ assertFalse(snap.startsWith(repeat('1', chunk)), "oldest chunk must be evicted");
+ assertTrue(snap.length() <= BoundedLog.MAX_CHARS, "total retained must stay under the cap");
+ }
+
+ @Test
+ public void alwaysKeepsAtLeastOneChunkEvenWhenOverCap() {
+ // A single append larger than MAX_CHARS is never evicted (size > 1 guard).
+ final BoundedLog log = new BoundedLog();
+ final String huge = repeat('z', BoundedLog.MAX_CHARS + 100);
+ log.append(huge);
+ assertEquals(huge.length(), log.snapshot().length());
+ }
+
+ private static String repeat(final char c, final int n) {
+ final char[] arr = new char[n];
+ Arrays.fill(arr, c);
+ return new String(arr);
+ }
+}
diff --git a/plugin/src/test/java/copilotj/awt/ActionBuilderTest.java b/plugin/src/test/java/copilotj/awt/ActionBuilderTest.java
new file mode 100644
index 00000000..01cd69cc
--- /dev/null
+++ b/plugin/src/test/java/copilotj/awt/ActionBuilderTest.java
@@ -0,0 +1,95 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.awt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import copilotj.awt.Action.Builder;
+
+/**
+ * Unit tests for {@link Action.Builder} construction and {@link Action} assembly.
+ */
+public class ActionBuilderTest {
+
+ @Test
+ public void builderRejectsNullType() {
+ assertThrows(IllegalStateException.class, () -> Action.builder(null, "n", "d"));
+ }
+
+ @Test
+ public void builderRejectsNullName() {
+ assertThrows(IllegalStateException.class, () -> Action.builder("t", null, "d"));
+ }
+
+ @Test
+ public void builderRejectsNullDescription() {
+ assertThrows(IllegalStateException.class, () -> Action.builder("t", "n", null));
+ }
+
+ @Test
+ public void builderAcceptsAllNonNull() {
+ assertNotNull(Action.builder("t", "n", "d"));
+ }
+
+ @Test
+ public void buildCopiesFieldsAndStartsEmpty() {
+ final Action a = Action.builder("t", "n", "d").build();
+ assertEquals("t", a.type);
+ assertEquals("n", a.name);
+ assertEquals("d", a.description);
+ assertTrue(a.parameters.isEmpty());
+ assertTrue(a.path.isEmpty());
+ }
+
+ @Test
+ public void addStringParameterEnumOverloadRejectsNull() {
+ final Builder b = Action.builder("t", "n", "d");
+ assertThrows(IllegalArgumentException.class, () -> b.addStringParameter("p", "d", (List) null));
+ }
+
+ @Test
+ public void addStringParameterEnumOverloadRejectsEmpty() {
+ final Builder b = Action.builder("t", "n", "d");
+ assertThrows(IllegalArgumentException.class, () -> b.addStringParameter("p", "d", Collections.emptyList()));
+ }
+
+ @Test
+ public void addPathAppendsInPlace() {
+ final Action a = Action.builder("t", "n", "d").build();
+ a.addPath("x");
+ a.addPath("y");
+ assertEquals(Arrays.asList("x", "y"), a.path);
+ }
+
+ @Test
+ public void descriptionIsMutableOnBuiltAction() {
+ // description is the only non-final Action field.
+ final Action a = Action.builder("t", "n", "d").build();
+ a.description = "new";
+ assertEquals("new", a.description);
+ }
+
+ @Test
+ public void chainingReturnsSelfAndBuilds() {
+ final Action a = Action.builder("t", "n", "d")
+ .addStringParameter("s", "desc")
+ .addBooleanParameter("b", "desc")
+ .addIntegerParameter("i", "desc")
+ .addNumberParameter("n2", "desc")
+ .build();
+ assertEquals(4, a.parameters.size());
+ }
+}
diff --git a/plugin/src/test/java/copilotj/awt/ParameterSchemaTest.java b/plugin/src/test/java/copilotj/awt/ParameterSchemaTest.java
new file mode 100644
index 00000000..cf5221b2
--- /dev/null
+++ b/plugin/src/test/java/copilotj/awt/ParameterSchemaTest.java
@@ -0,0 +1,103 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.awt;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Constructor-invariant tests for the {@link Action.ParameterSchema} hierarchy.
+ * Schemas are constructed directly (not via the Builder) to exercise the
+ * constructors' own validation.
+ */
+public class ParameterSchemaTest {
+
+ // --- base ParameterSchema name validation ---
+
+ @Test
+ public void baseRejectsNullName() {
+ assertThrows(IllegalArgumentException.class, () -> new Action.BooleanParameterSchema(null, "d"));
+ }
+
+ @Test
+ public void baseRejectsEmptyName() {
+ assertThrows(IllegalArgumentException.class, () -> new Action.BooleanParameterSchema("", "d"));
+ }
+
+ // --- StringParameterSchema bounds ---
+
+ @Test
+ public void stringRejectsNegativeMinLength() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new Action.StringParameterSchema("p", "d", -1, null, null, null));
+ }
+
+ @Test
+ public void stringRejectsNegativeMaxLength() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new Action.StringParameterSchema("p", "d", null, -5, null, null));
+ }
+
+ @Test
+ public void stringRejectsInvertedMinMax() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new Action.StringParameterSchema("p", "d", 5, 2, null, null));
+ }
+
+ @Test
+ public void stringAcceptsNullOptionals() {
+ assertNotNull(new Action.StringParameterSchema("p", "d", null, null, null, null));
+ }
+
+ // Regression guard: enum-list null entries are rejected at construction (the guard was
+ // previously inverted dead code). Blank strings are ACCEPTED because they are legitimate
+ // widget labels (AWT Choice/List placeholder items) — see ChoiceNode/ListNode.getActions().
+
+ @Test
+ public void stringRejectsEnumListWithNullEntry() {
+ final List bad = Arrays.asList("ok", null);
+ assertThrows(IllegalArgumentException.class,
+ () -> new Action.StringParameterSchema("p", "d", null, null, null, bad));
+ }
+
+ @Test
+ public void stringAcceptsEnumListWithBlankEntry() {
+ // Blank entries must be accepted: ChoiceNode/ListNode pass AWT item lists here and a
+ // blank placeholder item is valid. Rejecting it would crash getActions()/snapshot generation.
+ final List vals = Arrays.asList("ok", "");
+ assertNotNull(new Action.StringParameterSchema("p", "d", null, null, null, vals));
+ }
+
+ // --- IntegerParameterSchema ---
+
+ @Test
+ public void integerRejectsInvertedMinMax() {
+ assertThrows(IllegalArgumentException.class, () -> new Action.IntegerParameterSchema("p", "d", 10, 1));
+ }
+
+ @Test
+ public void integerAcceptsNullBounds() {
+ assertNotNull(new Action.IntegerParameterSchema("p", "d", null, null));
+ }
+
+ // --- NumberParameterSchema ---
+
+ @Test
+ public void numberRejectsInvertedMinMax() {
+ assertThrows(IllegalArgumentException.class, () -> new Action.NumberParameterSchema("p", "d", 10.0, 1.0));
+ }
+
+ @Test
+ public void numberAcceptsNullBounds() {
+ assertNotNull(new Action.NumberParameterSchema("p", "d", null, null));
+ }
+}
diff --git a/plugin/src/test/java/copilotj/awt/ParameterSchemaValidateTest.java b/plugin/src/test/java/copilotj/awt/ParameterSchemaValidateTest.java
new file mode 100644
index 00000000..df0f7d5a
--- /dev/null
+++ b/plugin/src/test/java/copilotj/awt/ParameterSchemaValidateTest.java
@@ -0,0 +1,162 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.awt;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link Action.ParameterSchema#validate(Object)} across all four
+ * subclasses. Contract: a valid value returns {@code null}; an invalid value
+ * returns a non-empty error list (never an empty list).
+ */
+public class ParameterSchemaValidateTest {
+
+ private static void assertInvalid(final List errors, final String containsFragment) {
+ assertNotNull(errors, "invalid value must return a non-null error list");
+ assertFalse(errors.isEmpty(), "invalid value must return a non-empty error list");
+ if (containsFragment != null) {
+ assertTrue(
+ errors.stream().anyMatch(e -> e.contains(containsFragment)),
+ "expected an error containing \"" + containsFragment + "\", got " + errors);
+ }
+ }
+
+ // ===== String =====
+
+ @Test
+ public void stringValidReturnsNull() {
+ assertNull(new Action.StringParameterSchema("p", "d", null, null, null, null).validate("ok"));
+ }
+
+ @Test
+ public void stringNullReturnsTypeError() {
+ assertInvalid(new Action.StringParameterSchema("p", "d", null, null, null, null).validate(null), "cannot be null");
+ }
+
+ @Test
+ public void stringWrongTypeReportsSimpleClassName() {
+ final List errors =
+ new Action.StringParameterSchema("p", "d", null, null, null, null).validate(Integer.valueOf(5));
+ assertInvalid(errors, "got Integer");
+ assertEquals(1, errors.size(), "type mismatch returns early with a single error");
+ }
+
+ @Test
+ public void stringTooShort() {
+ assertInvalid(new Action.StringParameterSchema("p", "d", 3, null, null, null).validate("ab"), "less than minimum");
+ }
+
+ @Test
+ public void stringTooLong() {
+ assertInvalid(new Action.StringParameterSchema("p", "d", null, 3, null, null).validate("abcd"), "greater than maximum");
+ }
+
+ @Test
+ public void stringPatternMismatch() {
+ assertInvalid(new Action.StringParameterSchema("p", "d", null, null, "[0-9]+", null).validate("abc"),
+ "does not match pattern");
+ }
+
+ @Test
+ public void stringNotInEnum() {
+ assertInvalid(new Action.StringParameterSchema("p", "d", null, null, null, Arrays.asList("a", "b")).validate("c"),
+ "not in the allowed enum");
+ }
+
+ @Test
+ public void stringEnumBoundaryValid() {
+ assertNull(new Action.StringParameterSchema("p", "d", null, null, null, Arrays.asList("a", "b")).validate("a"));
+ }
+
+ // ===== Integer =====
+
+ @Test
+ public void integerValidReturnsNull() {
+ assertNull(new Action.IntegerParameterSchema("p", "d", null, null).validate(Integer.valueOf(3)));
+ }
+
+ @Test
+ public void integerNullReturnsTypeError() {
+ assertInvalid(new Action.IntegerParameterSchema("p", "d", null, null).validate(null), "cannot be null");
+ }
+
+ @Test
+ public void integerRejectsLong() {
+ // Only java.lang.Integer is accepted; a Long reports a type error.
+ final List errors =
+ new Action.IntegerParameterSchema("p", "d", null, null).validate(Long.valueOf(3L));
+ assertInvalid(errors, "got Long");
+ }
+
+ @Test
+ public void integerBelowMinimum() {
+ assertInvalid(new Action.IntegerParameterSchema("p", "d", 0, null).validate(Integer.valueOf(-1)), "less than minimum");
+ }
+
+ @Test
+ public void integerAboveMaximum() {
+ assertInvalid(new Action.IntegerParameterSchema("p", "d", null, 10).validate(Integer.valueOf(11)), "greater than maximum");
+ }
+
+ // ===== Number =====
+
+ @Test
+ public void numberAcceptsInteger() {
+ assertNull(new Action.NumberParameterSchema("p", "d", null, null).validate(Integer.valueOf(3)));
+ }
+
+ @Test
+ public void numberAcceptsDouble() {
+ assertNull(new Action.NumberParameterSchema("p", "d", null, null).validate(Double.valueOf(3.5)));
+ }
+
+ @Test
+ public void numberNullReturnsTypeError() {
+ assertInvalid(new Action.NumberParameterSchema("p", "d", null, null).validate(null), "cannot be null");
+ }
+
+ @Test
+ public void numberBelowMinimum() {
+ assertInvalid(new Action.NumberParameterSchema("p", "d", 0.0, null).validate(Double.valueOf(-1.0)), "less than minimum");
+ }
+
+ @Test
+ public void numberAboveMaximum() {
+ assertInvalid(new Action.NumberParameterSchema("p", "d", null, 1.0).validate(Double.valueOf(2.5)), "greater than maximum");
+ }
+
+ // ===== Boolean (regression: valid -> null, NOT an empty list) =====
+
+ @Test
+ public void booleanValidTrueReturnsNull() {
+ assertNull(new Action.BooleanParameterSchema("p", "d").validate(Boolean.TRUE));
+ }
+
+ @Test
+ public void booleanValidFalseReturnsNull() {
+ assertNull(new Action.BooleanParameterSchema("p", "d").validate(Boolean.FALSE));
+ }
+
+ @Test
+ public void booleanNullReturnsTypeError() {
+ assertInvalid(new Action.BooleanParameterSchema("p", "d").validate(null), "cannot be null");
+ }
+
+ @Test
+ public void booleanWrongTypeReportsSimpleClassName() {
+ assertInvalid(new Action.BooleanParameterSchema("p", "d").validate("yes"), "got String");
+ }
+}
diff --git a/plugin/src/test/java/copilotj/util/FlexibleLocalDateTimeDeserializerTest.java b/plugin/src/test/java/copilotj/util/FlexibleLocalDateTimeDeserializerTest.java
new file mode 100644
index 00000000..57be5ceb
--- /dev/null
+++ b/plugin/src/test/java/copilotj/util/FlexibleLocalDateTimeDeserializerTest.java
@@ -0,0 +1,102 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * Unit tests for {@link FlexibleLocalDateTimeDeserializer}. Exercised through a
+ * real {@link ObjectMapper} (no mock) so the production {@code JsonParser} path
+ * runs. Expected offset-aware values are computed via the same chain the
+ * implementation uses, so the assertions are zone-agnostic.
+ */
+public class FlexibleLocalDateTimeDeserializerTest {
+
+ /** Plain static holder (NOT a record — tests compile under Java 8). */
+ public static final class Holder {
+ @JsonProperty("t")
+ public LocalDateTime t;
+ }
+
+ private static ObjectMapper mapper() {
+ final ObjectMapper m = new ObjectMapper();
+ final SimpleModule module = new SimpleModule();
+ module.addDeserializer(LocalDateTime.class, new FlexibleLocalDateTimeDeserializer());
+ m.registerModule(module);
+ return m;
+ }
+
+ private static LocalDateTime parse(final String jsonValue) throws Exception {
+ return mapper().readValue("{\"t\":\"" + jsonValue + "\"}", Holder.class).t;
+ }
+
+ private static LocalDateTime localAtSystemZone(final String offsetDateTime) {
+ return OffsetDateTime.parse(offsetDateTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+ }
+
+ @Test
+ public void parsesNaiveLocalDateTime() throws Exception {
+ assertEquals(LocalDateTime.of(2026, 6, 18, 10, 0, 0), parse("2026-06-18T10:00:00"));
+ }
+
+ @Test
+ public void parsesUtcZ() throws Exception {
+ final String s = "2026-06-18T10:00:00Z";
+ assertEquals(localAtSystemZone(s), parse(s));
+ }
+
+ @Test
+ public void parsesExplicitPositiveOffset() throws Exception {
+ final String s = "2026-06-18T18:00:00+08:00";
+ assertEquals(localAtSystemZone(s), parse(s));
+ }
+
+ @Test
+ public void parsesNegativeOffset() throws Exception {
+ final String s = "2026-06-18T05:00:00-05:00";
+ assertEquals(localAtSystemZone(s), parse(s));
+ }
+
+ @Test
+ public void trimsWhitespace() throws Exception {
+ assertEquals(LocalDateTime.of(2026, 6, 18, 10, 0, 0), parse(" 2026-06-18T10:00:00 "));
+ }
+
+ @Test
+ public void returnsNullForJsonNull() throws Exception {
+ final Holder h = mapper().readValue("{\"t\":null}", Holder.class);
+ assertNull(h.t);
+ }
+
+ @Test
+ public void returnsNullForEmptyAfterTrim() throws Exception {
+ assertNull(parse(" "));
+ }
+
+ @Test
+ public void rejectsDateOnly() {
+ // No time component -> LocalDateTime.parse throws, propagating out of readValue.
+ assertThrows(Exception.class, () -> parse("2026-06-18"));
+ }
+
+ @Test
+ public void rejectsGarbage() {
+ assertThrows(Exception.class, () -> parse("not-a-date"));
+ }
+}
diff --git a/plugin/src/test/java/copilotj/util/FromToTest.java b/plugin/src/test/java/copilotj/util/FromToTest.java
new file mode 100644
index 00000000..07f2027b
--- /dev/null
+++ b/plugin/src/test/java/copilotj/util/FromToTest.java
@@ -0,0 +1,56 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link FromTo}, a minimal generic value pair with no validation
+ * and no defined {@code equals}.
+ */
+public class FromToTest {
+
+ @Test
+ public void holdsFromAndToValues() {
+ final FromTo ft = new FromTo<>(1, 2);
+ assertEquals(1, ft.from);
+ assertEquals(2, ft.to);
+ }
+
+ @Test
+ public void acceptsNullOnBothEnds() {
+ final FromTo ft = new FromTo<>(null, null);
+ assertNull(ft.from);
+ assertNull(ft.to);
+ }
+
+ @Test
+ public void acceptsNullOnOneEnd() {
+ final FromTo ft = new FromTo<>(null, "x");
+ assertNull(ft.from);
+ assertEquals("x", ft.to);
+ }
+
+ @Test
+ public void worksAsStringPair() {
+ final FromTo ft = new FromTo<>("a", "b");
+ assertEquals("a", ft.from);
+ assertEquals("b", ft.to);
+ }
+
+ @Test
+ public void distinctInstancesAreNotEqualByReference() {
+ // FromTo defines no equals; two equal-content instances remain distinct references.
+ final FromTo a = new FromTo<>(1, 2);
+ final FromTo b = new FromTo<>(1, 2);
+ assertNotSame(a, b);
+ }
+}
diff --git a/plugin/src/test/java/copilotj/util/JsonBase64ImageTruncatorTest.java b/plugin/src/test/java/copilotj/util/JsonBase64ImageTruncatorTest.java
new file mode 100644
index 00000000..885f80d6
--- /dev/null
+++ b/plugin/src/test/java/copilotj/util/JsonBase64ImageTruncatorTest.java
@@ -0,0 +1,101 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.util;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link JsonBase64ImageTruncator}, a pure regex/string utility
+ * with no ImageJ coupling.
+ */
+public class JsonBase64ImageTruncatorTest {
+
+ private static final String LONG = "AAAAAAAAAAAAAAAAAAAA"; // 20 chars
+
+ @Test
+ public void truncatesLongBase64Content() {
+ final String json = "{\"img\":\"data:image/png;base64," + LONG + "\"}";
+ // maxLength 16 -> keep max(0, 16-3)=13 chars, then append "...".
+ final String expected = "{\"img\":\"data:image/png;base64,AAAAAAAAAAAAA...\"}";
+ assertEquals(expected, new JsonBase64ImageTruncator(json, 16).toString());
+ }
+
+ @Test
+ public void leavesShortContentUntouched() {
+ final String content = "AAAAAAAAAA"; // 10 chars, <= 16
+ final String json = "{\"img\":\"data:image/png;base64," + content + "\"}";
+ assertEquals(json, new JsonBase64ImageTruncator(json, 16).toString());
+ }
+
+ @Test
+ public void contentExactlyAtMaxLengthIsUntouched() {
+ // Production truncates only when content.length() > maxLength (strict). Pin the
+ // exact boundary so an off-by-one flip to >= would be caught.
+ final String content = "AAAAAAAAAAAAAAAA"; // 16 chars == maxLength
+ final String json = "{\"img\":\"data:image/png;base64," + content + "\"}";
+ assertEquals(json, new JsonBase64ImageTruncator(json, 16).toString());
+ }
+
+ @Test
+ public void noMatchReturnsOriginal() {
+ final String json = "{\"msg\":\"hello world\"}";
+ assertEquals(json, new JsonBase64ImageTruncator(json, 16).toString());
+ }
+
+ @Test
+ public void truncatesEveryMatch() {
+ final String json = "{\"a\":\"data:image/png;base64," + LONG + "\","
+ + "\"b\":\"data:image/jpeg;base64," + LONG + "\"}";
+ final String result = new JsonBase64ImageTruncator(json, 16).toString();
+ assertEquals(2, countOccurrences(result, "..."));
+ }
+
+ @Test
+ public void maxLengthBelowThreeCollapsesToEllipsis() {
+ final String json = "{\"img\":\"data:image/png;base64," + LONG + "\"}";
+ // max(0, 2-3)=0 -> substring(0,0) + "...".
+ assertEquals("{\"img\":\"data:image/png;base64,...\"}",
+ new JsonBase64ImageTruncator(json, 2).toString());
+ }
+
+ @Test
+ public void withTitlePrependsTitle() {
+ final String json = "{\"img\":\"data:image/png;base64," + LONG + "\"}";
+ // WithTitle hardcodes maxLength = 16.
+ final String result = new JsonBase64ImageTruncator.WithTitle("img", json).toString();
+ assertEquals("img: {\"img\":\"data:image/png;base64,AAAAAAAAAAAAA...\"}", result);
+ }
+
+ @Test
+ public void staticHelperMatchesInstance() {
+ final String json = "{\"img\":\"data:image/png;base64," + LONG + "\"}";
+ final String viaStatic = JsonBase64ImageTruncator.truncateJsonBase64(json, 16);
+ final String viaInstance = new JsonBase64ImageTruncator(json, 16).toString();
+ assertEquals(viaInstance, viaStatic);
+ }
+
+ @Test
+ public void svgXmlMimeSubtypeIsNotMatchedAndPassesThrough() {
+ // The regex matches only image/\w+ (single-token MIME), so a structured subtype
+ // like image/svg+xml does NOT match and is left intact. Known limitation;
+ // broadening the regex is tracked as a follow-up.
+ final String json = "{\"img\":\"data:image/svg+xml;base64," + LONG + "\"}";
+ assertEquals(json, new JsonBase64ImageTruncator(json, 4).toString());
+ }
+
+ private static int countOccurrences(final String haystack, final String needle) {
+ int count = 0;
+ int idx = 0;
+ while ((idx = haystack.indexOf(needle, idx)) != -1) {
+ count++;
+ idx += needle.length();
+ }
+ return count;
+ }
+}
diff --git a/plugin/src/test/java/copilotj/util/TrieTest.java b/plugin/src/test/java/copilotj/util/TrieTest.java
new file mode 100644
index 00000000..13983ffd
--- /dev/null
+++ b/plugin/src/test/java/copilotj/util/TrieTest.java
@@ -0,0 +1,129 @@
+/**
+ * SPDX-FileCopyrightText: Copyright contributors to the CopilotJ project.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package copilotj.util;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+import copilotj.util.Trie.CharTrie;
+import copilotj.util.Trie.SentenceTrie;
+import copilotj.util.Trie.WordTrie;
+
+/**
+ * Unit tests for the pure-logic {@link Trie} data structure. {@link Trie} has no
+ * ImageJ/AWT/runtime coupling, so it can be exercised directly.
+ */
+// Raw Trie.Map / Trie.SimpleMap are inner generic types; unchecked access is by design here.
+@SuppressWarnings("rawtypes")
+public class TrieTest {
+
+ @Test
+ public void charTrieInsertSearchAndPrefix() {
+ final CharTrie trie = new CharTrie();
+ trie.insert("apple");
+
+ assertTrue(trie.search("apple"), "exact sentence should be found");
+ assertFalse(trie.search("app"), "non-leaf prefix should not satisfy search");
+ assertTrue(trie.startsWith("app"), "prefix should be detected");
+ assertFalse(trie.startsWith("b"), "unknown prefix");
+ assertFalse(new CharTrie().search("x"), "empty trie lookup handled");
+ }
+
+ @Test
+ public void charTrieSharedPrefixDoesNotInterfere() {
+ final CharTrie trie = new CharTrie();
+ trie.insert("app");
+ trie.insert("apple");
+
+ assertTrue(trie.search("app"));
+ assertTrue(trie.search("apple"));
+ assertFalse(trie.search("ap"));
+ }
+
+ @Test
+ public void duplicateInsertIsIdempotent() {
+ final CharTrie trie = new CharTrie();
+ trie.insert("abc");
+ trie.insert("abc");
+
+ assertTrue(trie.search("abc"));
+ }
+
+ @Test
+ public void wordTrieSearchAndPrefix() {
+ final WordTrie trie = new WordTrie();
+ trie.insert(new String[] {"a", "b", "c"});
+
+ assertTrue(trie.search(new String[] {"a", "b", "c"}));
+ assertFalse(trie.search(new String[] {"a", "b"}));
+ assertTrue(trie.startsWith(new String[] {"a", "b"}));
+ assertFalse(trie.startsWith(new String[] {"x"}));
+ }
+
+ @Test
+ public void wordTrieToMapWithFlattenCollapsesSingleChildChains() {
+ // root -> a -> b -> {c (leaf), d (leaf)}; the single-child a->b chain is
+ // flattened into the combined key "ab".
+ final WordTrie trie = new WordTrie();
+ trie.insert(new String[] {"a", "b", "c"});
+ trie.insert(new String[] {"a", "b", "d"});
+
+ final Trie.Map flattened = trie.toMapWithFlatten();
+ assertNotNull(flattened.children);
+ assertTrue(flattened.children.containsKey("ab"), "flattened single-child chain should produce key 'ab'");
+
+ final Trie.Map ab = (Trie.Map) flattened.children.get("ab");
+ assertNotNull(ab.children);
+ assertTrue(ab.children.containsKey("c"));
+ assertTrue(ab.children.containsKey("d"));
+ assertTrue(((Trie.Map) ab.children.get("c")).isLeaf, "leaf node c should be marked");
+ }
+
+ @Test
+ public void wordTrieToSimpleMapWithFlattenCollapsesSingleChildChains() {
+ // nodeToSimpleMap flattens single-child chains via a while-loop (distinct from
+ // toMap's single-edge flatten). This is the path that previously held a debug
+ // println, so it is covered explicitly here.
+ final WordTrie trie = new WordTrie();
+ trie.insert(new String[] {"a", "b", "c"});
+ trie.insert(new String[] {"a", "b", "d"});
+
+ final Trie.SimpleMap flattened = trie.toSimpleMapWithFlatten();
+ assertTrue(flattened.containsKey("ab"), "flattened single-child chain should produce key 'ab'");
+
+ final Trie.SimpleMap ab = (Trie.SimpleMap) flattened.get("ab");
+ assertTrue(ab.containsKey("c"));
+ assertTrue(ab.containsKey("d"));
+ }
+
+ @Test
+ public void sentenceTrieSplitsOnDelimiters() {
+ // Delimiters stick to the preceding token: "a/b.c" -> ["a/", "b.", "c"].
+ final SentenceTrie trie = new SentenceTrie(new char[] {'/', '.'});
+ trie.insert("a/b.c");
+
+ assertTrue(trie.search("a/b.c"), "full sentence reconstructs");
+ assertFalse(trie.search("a/b"), "trailing-delimiter token is significant");
+ assertTrue(trie.startsWith("a/"), "prefix token including its delimiter");
+ assertTrue(trie.startsWith("a/b."), "prefix across two tokens");
+ assertFalse(trie.startsWith("a/b"));
+ }
+
+ @Test
+ public void toMapWithFlattenRejectsNullCombiner() {
+ assertThrows(IllegalArgumentException.class, () -> new WordTrie().toMapWithFlatten(null));
+ }
+
+ @Test
+ public void toSimpleMapWithFlattenRejectsNullCombiner() {
+ assertThrows(IllegalArgumentException.class, () -> new WordTrie().toSimpleMapWithFlatten(null));
+ }
+}
diff --git a/uv.lock b/uv.lock
index 485f57c9..2d01cbf6 100644
--- a/uv.lock
+++ b/uv.lock
@@ -4206,11 +4206,11 @@ wheels = [
[[package]]
name = "pypdf"
-version = "6.12.2"
+version = "6.13.3"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0a/6d/20879428577c1e57ecd41b69dc86beabf43db9287ad2e702207f8b48c751/pypdf-6.12.2.tar.gz", hash = "sha256:111669eb6680c04495ae0c113a1476e3bf93a95761d23c7406b591c80a6490b1", size = 6468184, upload-time = "2026-05-26T13:31:26.911Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/17/18/9947cc201af9ccf76720fd3347bf4f70eb882ce3fcf4cb05f7443e4cf871/pypdf-6.13.3.tar.gz", hash = "sha256:f3cb822769725f1bac658c406cfc9460399043f3750c2d3e4650e0a85eacabd7", size = 6484063, upload-time = "2026-06-17T15:22:00.898Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/9e/44/fee070a16639d9869bb6a7e0f3a1b3946da1d66f32b9260b4d19cb90d7b2/pypdf-6.12.2-py3-none-any.whl", hash = "sha256:67b2699357a1f3f4c945940ea80826349ee507c9e2577724a14b4941982c104d", size = 343865, upload-time = "2026-05-26T13:31:25.068Z" },
+ { url = "https://files.pythonhosted.org/packages/94/56/2967e621598987905fb8cdfadd8f8de6b5c68c9351f0523c4df8409f28f1/pypdf-6.13.3-py3-none-any.whl", hash = "sha256:c6e3f86afb625791510b02ad5480e94b63970bb957df75d44657c282ecc52224", size = 347288, upload-time = "2026-06-17T15:21:59.512Z" },
]
[[package]]
diff --git a/web/package.json b/web/package.json
index 06532830..13109875 100644
--- a/web/package.json
+++ b/web/package.json
@@ -37,7 +37,7 @@
"prettier-plugin-organize-imports": "^4.3.0",
"typescript": "~5.9.3",
"unplugin-vue-components": "^32.1.0",
- "vite": "^6.4.3",
+ "vite": "^8.0.12",
"vue-tsc": "^3.3.5"
},
"prettier": {
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index 128607e0..8e719b7a 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -16,16 +16,16 @@ importers:
version: 2.0.3
'@tabler/icons-vue':
specifier: ^3.44.0
- version: 3.44.0(vue@3.5.38(typescript@5.9.3))
+ version: 3.44.0(vue@3.5.39(typescript@5.9.3))
'@tailwindcss/typography':
specifier: ^0.5.20
version: 0.5.20(tailwindcss@4.3.1)
'@tailwindcss/vite':
specifier: ^4.3.1
- version: 4.3.1(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))
+ version: 4.3.1(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))
'@vueuse/core':
specifier: ^14.3.0
- version: 14.3.0(vue@3.5.38(typescript@5.9.3))
+ version: 14.3.0(vue@3.5.39(typescript@5.9.3))
date-fns:
specifier: ^4.4.0
version: 4.4.0
@@ -43,10 +43,10 @@ importers:
version: 2.2.4(marked@18.0.5)
pinia:
specifier: ^3.0.4
- version: 3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))
+ version: 3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
primevue:
specifier: ^4.5.5
- version: 4.5.5(vue@3.5.38(typescript@5.9.3))
+ version: 4.5.5(vue@3.5.39(typescript@5.9.3))
tailwind-merge:
specifier: ^3.6.0
version: 3.6.0
@@ -61,26 +61,26 @@ importers:
version: 14.0.0
vue:
specifier: ^3.5.38
- version: 3.5.38(typescript@5.9.3)
+ version: 3.5.39(typescript@5.9.3)
vue-router:
specifier: ^5.1.0
- version: 5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)))(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))
+ version: 5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)))(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3))
devDependencies:
'@primevue/auto-import-resolver':
specifier: ^4.5.5
version: 4.5.5
'@types/node':
specifier: ^26.0.0
- version: 26.0.0
+ version: 26.0.1
'@types/uuid':
specifier: ^11.0.0
version: 11.0.0
'@vitejs/plugin-vue':
specifier: ^6.0.7
- version: 6.0.7(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))
+ version: 6.0.7(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3))
'@vue/tsconfig':
specifier: ^0.9.1
- version: 0.9.1(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))
+ version: 0.9.1(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
prettier-plugin-organize-imports:
specifier: ^4.3.0
version: 4.3.0(prettier@3.6.0)(typescript@5.9.3)(vue-tsc@3.3.5(typescript@5.9.3))
@@ -89,10 +89,10 @@ importers:
version: 5.9.3
unplugin-vue-components:
specifier: ^32.1.0
- version: 32.1.0(vue@3.5.38(typescript@5.9.3))
+ version: 32.1.0(vue@3.5.39(typescript@5.9.3))
vite:
- specifier: ^6.4.3
- version: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
+ specifier: ^8.0.12
+ version: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)
vue-tsc:
specifier: ^3.3.5
version: 3.3.5(typescript@5.9.3)
@@ -137,161 +137,14 @@ packages:
resolution: {integrity: sha512-K8ponJDxBwDHigkeFqaqT5wLGl4bTlwMafR8k7b5CPxr6Ww+UG9ls8Yx6Tcpboxu97eeGVEEyKcHmEyOwN1vSw==}
engines: {node: ^22.18.0 || >=24.11.0}
- '@esbuild/aix-ppc64@0.25.12':
- resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.25.12':
- resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.25.12':
- resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.25.12':
- resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.25.12':
- resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.25.12':
- resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [darwin]
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
- '@esbuild/freebsd-arm64@0.25.12':
- resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [freebsd]
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
- '@esbuild/freebsd-x64@0.25.12':
- resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.25.12':
- resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.25.12':
- resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
- engines: {node: '>=18'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.25.12':
- resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.25.12':
- resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
- engines: {node: '>=18'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.25.12':
- resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
- engines: {node: '>=18'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.25.12':
- resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
- engines: {node: '>=18'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.25.12':
- resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
- engines: {node: '>=18'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.25.12':
- resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
- engines: {node: '>=18'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.25.12':
- resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-arm64@0.25.12':
- resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [netbsd]
-
- '@esbuild/netbsd-x64@0.25.12':
- resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-arm64@0.25.12':
- resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openbsd]
-
- '@esbuild/openbsd-x64@0.25.12':
- resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/openharmony-arm64@0.25.12':
- resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [openharmony]
-
- '@esbuild/sunos-x64@0.25.12':
- resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.25.12':
- resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
- engines: {node: '>=18'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.25.12':
- resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
- engines: {node: '>=18'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.25.12':
- resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
- engines: {node: '>=18'}
- cpu: [x64]
- os: [win32]
+ '@emnapi/wasi-threads@1.2.1':
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -309,6 +162,15 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+ '@napi-rs/wasm-runtime@1.1.5':
+ resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1
+ '@emnapi/runtime': ^1.7.1
+
+ '@oxc-project/types@0.133.0':
+ resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+
'@primeuix/styled@0.7.4':
resolution: {integrity: sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ==}
engines: {node: '>=12.11.0'}
@@ -341,133 +203,103 @@ packages:
resolution: {integrity: sha512-ZYLu9m3Nm5BmL0woqCJX84EZfLBepJn+T19v5oj3PlsMpUVNnDAsm2jgYdT6/b3RkdOuccxk4Pg/0EvlATOAhA==}
engines: {node: '>=12.11.0'}
- '@rolldown/pluginutils@1.0.1':
- resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
-
- '@rollup/rollup-android-arm-eabi@4.62.0':
- resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==}
- cpu: [arm]
- os: [android]
-
- '@rollup/rollup-android-arm64@4.62.0':
- resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==}
+ '@rolldown/binding-android-arm64@1.0.3':
+ resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@rollup/rollup-darwin-arm64@4.62.0':
- resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==}
+ '@rolldown/binding-darwin-arm64@1.0.3':
+ resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rollup/rollup-darwin-x64@4.62.0':
- resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==}
+ '@rolldown/binding-darwin-x64@1.0.3':
+ resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rollup/rollup-freebsd-arm64@4.62.0':
- resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==}
- cpu: [arm64]
- os: [freebsd]
-
- '@rollup/rollup-freebsd-x64@4.62.0':
- resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==}
+ '@rolldown/binding-freebsd-x64@1.0.3':
+ resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rollup/rollup-linux-arm-gnueabihf@4.62.0':
- resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm-musleabihf@4.62.0':
- resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
+ resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rollup/rollup-linux-arm64-gnu@4.62.0':
- resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==}
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
+ resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
- '@rollup/rollup-linux-arm64-musl@4.62.0':
- resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==}
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
+ resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
+ libc: [musl]
- '@rollup/rollup-linux-loong64-gnu@4.62.0':
- resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==}
- cpu: [loong64]
- os: [linux]
-
- '@rollup/rollup-linux-loong64-musl@4.62.0':
- resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==}
- cpu: [loong64]
- os: [linux]
-
- '@rollup/rollup-linux-ppc64-gnu@4.62.0':
- resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==}
- cpu: [ppc64]
- os: [linux]
-
- '@rollup/rollup-linux-ppc64-musl@4.62.0':
- resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==}
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
+ resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
+ libc: [glibc]
- '@rollup/rollup-linux-riscv64-gnu@4.62.0':
- resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-riscv64-musl@4.62.0':
- resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-s390x-gnu@4.62.0':
- resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==}
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
+ resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
+ libc: [glibc]
- '@rollup/rollup-linux-x64-gnu@4.62.0':
- resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==}
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
+ resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [glibc]
- '@rollup/rollup-linux-x64-musl@4.62.0':
- resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==}
+ '@rolldown/binding-linux-x64-musl@1.0.3':
+ resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
+ libc: [musl]
- '@rollup/rollup-openbsd-x64@4.62.0':
- resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==}
- cpu: [x64]
- os: [openbsd]
-
- '@rollup/rollup-openharmony-arm64@4.62.0':
- resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==}
+ '@rolldown/binding-openharmony-arm64@1.0.3':
+ resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rollup/rollup-win32-arm64-msvc@4.62.0':
- resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==}
- cpu: [arm64]
- os: [win32]
+ '@rolldown/binding-wasm32-wasi@1.0.3':
+ resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
- '@rollup/rollup-win32-ia32-msvc@4.62.0':
- resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==}
- cpu: [ia32]
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
+ resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
os: [win32]
- '@rollup/rollup-win32-x64-gnu@4.62.0':
- resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==}
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
+ resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
- '@rollup/rollup-win32-x64-msvc@4.62.0':
- resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==}
- cpu: [x64]
- os: [win32]
+ '@rolldown/pluginutils@1.0.1':
+ resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
'@tabler/icons-vue@3.44.0':
resolution: {integrity: sha512-mABxdhq3SWo2ZI77w/t0reiOGNim/SEDSlfMT5PeiWA3cZwnZoQUYRiq/X6SgeTaA7LzCTX0IuvQWVf4RWOvsg==}
@@ -515,24 +347,28 @@ packages:
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-arm64-musl@4.3.1':
resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-linux-x64-gnu@4.3.1':
resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
'@tailwindcss/oxide-linux-x64-musl@4.3.1':
resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
+ libc: [musl]
'@tailwindcss/oxide-wasm32-wasi@4.3.1':
resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==}
@@ -572,14 +408,14 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7 || ^8
- '@types/estree@1.0.9':
- resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+ '@tybys/wasm-util@0.10.2':
+ resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
- '@types/node@26.0.0':
- resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==}
+ '@types/node@26.0.1':
+ resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==}
'@types/uuid@11.0.0':
resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==}
@@ -613,56 +449,80 @@ packages:
vue:
optional: true
+ '@vue/compiler-core@3.5.30':
+ resolution: {integrity: sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==}
+
'@vue/compiler-core@3.5.38':
resolution: {integrity: sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==}
+ '@vue/compiler-core@3.5.39':
+ resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==}
+
+ '@vue/compiler-dom@3.5.30':
+ resolution: {integrity: sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==}
+
'@vue/compiler-dom@3.5.38':
resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==}
- '@vue/compiler-sfc@3.5.38':
- resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==}
+ '@vue/compiler-dom@3.5.39':
+ resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==}
+
+ '@vue/compiler-sfc@3.5.30':
+ resolution: {integrity: sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==}
- '@vue/compiler-ssr@3.5.38':
- resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==}
+ '@vue/compiler-sfc@3.5.39':
+ resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==}
+
+ '@vue/compiler-ssr@3.5.30':
+ resolution: {integrity: sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==}
+
+ '@vue/compiler-ssr@3.5.39':
+ resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==}
'@vue/devtools-api@7.7.9':
resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==}
- '@vue/devtools-api@8.1.3':
- resolution: {integrity: sha512-73NMCvxXh8Hyozc/jiwqTFWVcCMyi11U1zmrq4DoukQJnuo8JHt6FsNu4HdeUDa8SpIp5vb7Q22GWgIq0efsXg==}
+ '@vue/devtools-api@8.1.5':
+ resolution: {integrity: sha512-YJipMVAKe5wT5CWf5kTYCaNV7NMNjFVxJkIkJaJ4W/nCxEBzlZzrOsYKeCymdCrFZmBS/+wTWFoUs3Jf/Q6XSQ==}
'@vue/devtools-kit@7.7.9':
resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==}
- '@vue/devtools-kit@8.1.3':
- resolution: {integrity: sha512-cRn7GXiCQkMYU2Z3h3pM4YO/ndbx9FY1yLDAqIqPLcmIq4H6zAOJHein6tvZU3AfPwgrodqLiPBEF+YQaS8AxA==}
+ '@vue/devtools-kit@8.1.5':
+ resolution: {integrity: sha512-FcSAxsi4eWuXLCB7Rv9lj0aIVHHPNVQ2BazGf4RJTc2JCqb4BQg0hk87ZFhminCfl+mD5OUI0rX2cgyu4kJOGA==}
'@vue/devtools-shared@7.7.9':
resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==}
- '@vue/devtools-shared@8.1.3':
- resolution: {integrity: sha512-CM3uIPL+v+lrJUk33+pxspYo0MhuMWlCvf7zC9fybifvCPyM2jUbYRPwoYEJgYbwRqPikm5HozbUhp60MF2QuA==}
+ '@vue/devtools-shared@8.1.5':
+ resolution: {integrity: sha512-mhT4zcPFhF+Xk1O4BfhhrbXzpmfqY03fS6xGpcllbQG7lDjhQf8pQHcTIhqQIYx1hfwtHmk/6jM96ele0UxPqQ==}
'@vue/language-core@3.3.5':
resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==}
- '@vue/reactivity@3.5.38':
- resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==}
+ '@vue/reactivity@3.5.39':
+ resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==}
- '@vue/runtime-core@3.5.38':
- resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==}
+ '@vue/runtime-core@3.5.39':
+ resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==}
- '@vue/runtime-dom@3.5.38':
- resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==}
+ '@vue/runtime-dom@3.5.39':
+ resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==}
- '@vue/server-renderer@3.5.38':
- resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==}
+ '@vue/server-renderer@3.5.39':
+ resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==}
peerDependencies:
- vue: 3.5.38
+ vue: 3.5.39
+
+ '@vue/shared@3.5.30':
+ resolution: {integrity: sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==}
'@vue/shared@3.5.38':
resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==}
+ '@vue/shared@3.5.39':
+ resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==}
+
'@vue/tsconfig@0.9.1':
resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==}
peerDependencies:
@@ -687,8 +547,8 @@ packages:
peerDependencies:
vue: ^3.5.0
- acorn@8.17.0:
- resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -746,11 +606,6 @@ packages:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
- esbuild@0.25.12:
- resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
- engines: {node: '>=18'}
- hasBin: true
-
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
@@ -834,24 +689,28 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
+ libc: [musl]
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [glibc]
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
+ libc: [musl]
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
@@ -869,6 +728,10 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ local-pkg@1.1.2:
+ resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
+ engines: {node: '>=14'}
+
local-pkg@1.2.1:
resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==}
engines: {node: '>=14'}
@@ -893,6 +756,9 @@ packages:
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+ mlly@1.8.1:
+ resolution: {integrity: sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==}
+
mlly@1.8.2:
resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
@@ -939,8 +805,8 @@ packages:
pkg-types@1.3.1:
resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
- pkg-types@2.3.1:
- resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==}
+ pkg-types@2.3.0:
+ resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
@@ -979,9 +845,9 @@ packages:
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
- rollup@4.62.0:
- resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==}
- engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+ rolldown@1.0.3:
+ resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
scule@1.3.0:
@@ -1018,13 +884,16 @@ packages:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
- ufo@1.6.4:
- resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==}
+ ufo@1.6.3:
+ resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
undici-types@8.3.0:
resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==}
@@ -1054,31 +923,34 @@ packages:
resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==}
hasBin: true
- vite@6.4.3:
- resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
+ vite@8.0.16:
+ resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
+ '@types/node': ^20.19.0 || >=22.12.0
+ '@vitejs/devtools': ^0.1.18
+ esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0'
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- sass-embedded: '*'
- stylus: '*'
- sugarss: '*'
+ 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
peerDependenciesMeta:
'@types/node':
optional: true
+ '@vitejs/devtools':
+ optional: true
+ esbuild:
+ optional: true
jiti:
optional: true
less:
optional: true
- lightningcss:
- optional: true
sass:
optional: true
sass-embedded:
@@ -1121,8 +993,8 @@ packages:
peerDependencies:
typescript: '>=5.0.0'
- vue@3.5.38:
- resolution: {integrity: sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==}
+ vue@3.5.39:
+ resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
@@ -1174,82 +1046,20 @@ snapshots:
'@babel/helper-string-parser': 8.0.0
'@babel/helper-validator-identifier': 8.0.2
- '@esbuild/aix-ppc64@0.25.12':
- optional: true
-
- '@esbuild/android-arm64@0.25.12':
- optional: true
-
- '@esbuild/android-arm@0.25.12':
- optional: true
-
- '@esbuild/android-x64@0.25.12':
- optional: true
-
- '@esbuild/darwin-arm64@0.25.12':
- optional: true
-
- '@esbuild/darwin-x64@0.25.12':
- optional: true
-
- '@esbuild/freebsd-arm64@0.25.12':
- optional: true
-
- '@esbuild/freebsd-x64@0.25.12':
- optional: true
-
- '@esbuild/linux-arm64@0.25.12':
- optional: true
-
- '@esbuild/linux-arm@0.25.12':
- optional: true
-
- '@esbuild/linux-ia32@0.25.12':
- optional: true
-
- '@esbuild/linux-loong64@0.25.12':
- optional: true
-
- '@esbuild/linux-mips64el@0.25.12':
- optional: true
-
- '@esbuild/linux-ppc64@0.25.12':
- optional: true
-
- '@esbuild/linux-riscv64@0.25.12':
- optional: true
-
- '@esbuild/linux-s390x@0.25.12':
- optional: true
-
- '@esbuild/linux-x64@0.25.12':
- optional: true
-
- '@esbuild/netbsd-arm64@0.25.12':
- optional: true
-
- '@esbuild/netbsd-x64@0.25.12':
- optional: true
-
- '@esbuild/openbsd-arm64@0.25.12':
- optional: true
-
- '@esbuild/openbsd-x64@0.25.12':
- optional: true
-
- '@esbuild/openharmony-arm64@0.25.12':
- optional: true
-
- '@esbuild/sunos-x64@0.25.12':
- optional: true
-
- '@esbuild/win32-arm64@0.25.12':
+ '@emnapi/core@1.10.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
optional: true
- '@esbuild/win32-ia32@0.25.12':
+ '@emnapi/runtime@1.10.0':
+ dependencies:
+ tslib: 2.8.1
optional: true
- '@esbuild/win32-x64@0.25.12':
+ '@emnapi/wasi-threads@1.2.1':
+ dependencies:
+ tslib: 2.8.1
optional: true
'@jridgewell/gen-mapping@0.3.13':
@@ -1271,6 +1081,15 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@tybys/wasm-util': 0.10.2
+ optional: true
+
+ '@oxc-project/types@0.133.0': {}
+
'@primeuix/styled@0.7.4':
dependencies:
'@primeuix/utils': 0.6.4
@@ -1289,102 +1108,76 @@ snapshots:
dependencies:
'@primevue/metadata': 4.5.5
- '@primevue/core@4.5.5(vue@3.5.38(typescript@5.9.3))':
+ '@primevue/core@4.5.5(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@primeuix/styled': 0.7.4
'@primeuix/utils': 0.6.4
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
- '@primevue/icons@4.5.5(vue@3.5.38(typescript@5.9.3))':
+ '@primevue/icons@4.5.5(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@primeuix/utils': 0.6.4
- '@primevue/core': 4.5.5(vue@3.5.38(typescript@5.9.3))
+ '@primevue/core': 4.5.5(vue@3.5.39(typescript@5.9.3))
transitivePeerDependencies:
- vue
'@primevue/metadata@4.5.5': {}
- '@rolldown/pluginutils@1.0.1': {}
-
- '@rollup/rollup-android-arm-eabi@4.62.0':
- optional: true
-
- '@rollup/rollup-android-arm64@4.62.0':
- optional: true
-
- '@rollup/rollup-darwin-arm64@4.62.0':
- optional: true
-
- '@rollup/rollup-darwin-x64@4.62.0':
- optional: true
-
- '@rollup/rollup-freebsd-arm64@4.62.0':
- optional: true
-
- '@rollup/rollup-freebsd-x64@4.62.0':
- optional: true
-
- '@rollup/rollup-linux-arm-gnueabihf@4.62.0':
+ '@rolldown/binding-android-arm64@1.0.3':
optional: true
- '@rollup/rollup-linux-arm-musleabihf@4.62.0':
+ '@rolldown/binding-darwin-arm64@1.0.3':
optional: true
- '@rollup/rollup-linux-arm64-gnu@4.62.0':
+ '@rolldown/binding-darwin-x64@1.0.3':
optional: true
- '@rollup/rollup-linux-arm64-musl@4.62.0':
+ '@rolldown/binding-freebsd-x64@1.0.3':
optional: true
- '@rollup/rollup-linux-loong64-gnu@4.62.0':
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
optional: true
- '@rollup/rollup-linux-loong64-musl@4.62.0':
+ '@rolldown/binding-linux-arm64-gnu@1.0.3':
optional: true
- '@rollup/rollup-linux-ppc64-gnu@4.62.0':
+ '@rolldown/binding-linux-arm64-musl@1.0.3':
optional: true
- '@rollup/rollup-linux-ppc64-musl@4.62.0':
+ '@rolldown/binding-linux-ppc64-gnu@1.0.3':
optional: true
- '@rollup/rollup-linux-riscv64-gnu@4.62.0':
+ '@rolldown/binding-linux-s390x-gnu@1.0.3':
optional: true
- '@rollup/rollup-linux-riscv64-musl@4.62.0':
+ '@rolldown/binding-linux-x64-gnu@1.0.3':
optional: true
- '@rollup/rollup-linux-s390x-gnu@4.62.0':
+ '@rolldown/binding-linux-x64-musl@1.0.3':
optional: true
- '@rollup/rollup-linux-x64-gnu@4.62.0':
+ '@rolldown/binding-openharmony-arm64@1.0.3':
optional: true
- '@rollup/rollup-linux-x64-musl@4.62.0':
- optional: true
-
- '@rollup/rollup-openbsd-x64@4.62.0':
- optional: true
-
- '@rollup/rollup-openharmony-arm64@4.62.0':
- optional: true
-
- '@rollup/rollup-win32-arm64-msvc@4.62.0':
+ '@rolldown/binding-wasm32-wasi@1.0.3':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
optional: true
- '@rollup/rollup-win32-ia32-msvc@4.62.0':
+ '@rolldown/binding-win32-arm64-msvc@1.0.3':
optional: true
- '@rollup/rollup-win32-x64-gnu@4.62.0':
+ '@rolldown/binding-win32-x64-msvc@1.0.3':
optional: true
- '@rollup/rollup-win32-x64-msvc@4.62.0':
- optional: true
+ '@rolldown/pluginutils@1.0.1': {}
- '@tabler/icons-vue@3.44.0(vue@3.5.38(typescript@5.9.3))':
+ '@tabler/icons-vue@3.44.0(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@tabler/icons': 3.44.0
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
'@tabler/icons@3.44.0': {}
@@ -1454,18 +1247,21 @@ snapshots:
postcss-selector-parser: 6.0.10
tailwindcss: 4.3.1
- '@tailwindcss/vite@4.3.1(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))':
+ '@tailwindcss/vite@4.3.1(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))':
dependencies:
'@tailwindcss/node': 4.3.1
'@tailwindcss/oxide': 4.3.1
tailwindcss: 4.3.1
- vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
+ vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)
- '@types/estree@1.0.9': {}
+ '@tybys/wasm-util@0.10.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
'@types/jsesc@2.5.1': {}
- '@types/node@26.0.0':
+ '@types/node@26.0.1':
dependencies:
undici-types: 8.3.0
@@ -1475,11 +1271,11 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
- '@vitejs/plugin-vue@6.0.7(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))':
+ '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
- vue: 3.5.38(typescript@5.9.3)
+ vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)
+ vue: 3.5.39(typescript@5.9.3)
'@volar/language-core@2.4.28':
dependencies:
@@ -1493,15 +1289,23 @@ snapshots:
path-browserify: 1.0.1
vscode-uri: 3.1.0
- '@vue-macros/common@3.1.2(vue@3.5.38(typescript@5.9.3))':
+ '@vue-macros/common@3.1.2(vue@3.5.39(typescript@5.9.3))':
dependencies:
- '@vue/compiler-sfc': 3.5.38
+ '@vue/compiler-sfc': 3.5.30
ast-kit: 2.2.0
- local-pkg: 1.2.1
+ local-pkg: 1.1.2
magic-string-ast: 1.0.3
unplugin-utils: 0.3.1
optionalDependencies:
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
+
+ '@vue/compiler-core@3.5.30':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@vue/shared': 3.5.30
+ entities: 7.0.1
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
'@vue/compiler-core@3.5.38':
dependencies:
@@ -1511,35 +1315,70 @@ snapshots:
estree-walker: 2.0.2
source-map-js: 1.2.1
+ '@vue/compiler-core@3.5.39':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@vue/shared': 3.5.39
+ entities: 7.0.1
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.30':
+ dependencies:
+ '@vue/compiler-core': 3.5.30
+ '@vue/shared': 3.5.30
+
'@vue/compiler-dom@3.5.38':
dependencies:
'@vue/compiler-core': 3.5.38
'@vue/shared': 3.5.38
- '@vue/compiler-sfc@3.5.38':
+ '@vue/compiler-dom@3.5.39':
+ dependencies:
+ '@vue/compiler-core': 3.5.39
+ '@vue/shared': 3.5.39
+
+ '@vue/compiler-sfc@3.5.30':
dependencies:
'@babel/parser': 7.29.7
- '@vue/compiler-core': 3.5.38
- '@vue/compiler-dom': 3.5.38
- '@vue/compiler-ssr': 3.5.38
- '@vue/shared': 3.5.38
+ '@vue/compiler-core': 3.5.30
+ '@vue/compiler-dom': 3.5.30
+ '@vue/compiler-ssr': 3.5.30
+ '@vue/shared': 3.5.30
estree-walker: 2.0.2
magic-string: 0.30.21
postcss: 8.5.15
source-map-js: 1.2.1
- '@vue/compiler-ssr@3.5.38':
+ '@vue/compiler-sfc@3.5.39':
dependencies:
- '@vue/compiler-dom': 3.5.38
- '@vue/shared': 3.5.38
+ '@babel/parser': 7.29.7
+ '@vue/compiler-core': 3.5.39
+ '@vue/compiler-dom': 3.5.39
+ '@vue/compiler-ssr': 3.5.39
+ '@vue/shared': 3.5.39
+ estree-walker: 2.0.2
+ magic-string: 0.30.21
+ postcss: 8.5.15
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.30':
+ dependencies:
+ '@vue/compiler-dom': 3.5.30
+ '@vue/shared': 3.5.30
+
+ '@vue/compiler-ssr@3.5.39':
+ dependencies:
+ '@vue/compiler-dom': 3.5.39
+ '@vue/shared': 3.5.39
'@vue/devtools-api@7.7.9':
dependencies:
'@vue/devtools-kit': 7.7.9
- '@vue/devtools-api@8.1.3':
+ '@vue/devtools-api@8.1.5':
dependencies:
- '@vue/devtools-kit': 8.1.3
+ '@vue/devtools-kit': 8.1.5
'@vue/devtools-kit@7.7.9':
dependencies:
@@ -1551,9 +1390,9 @@ snapshots:
speakingurl: 14.0.1
superjson: 2.2.6
- '@vue/devtools-kit@8.1.3':
+ '@vue/devtools-kit@8.1.5':
dependencies:
- '@vue/devtools-shared': 8.1.3
+ '@vue/devtools-shared': 8.1.5
birpc: 2.9.0
hookable: 5.5.3
perfect-debounce: 2.1.0
@@ -1562,7 +1401,7 @@ snapshots:
dependencies:
rfdc: 1.4.1
- '@vue/devtools-shared@8.1.3': {}
+ '@vue/devtools-shared@8.1.5': {}
'@vue/language-core@3.3.5':
dependencies:
@@ -1574,49 +1413,53 @@ snapshots:
path-browserify: 1.0.1
picomatch: 4.0.4
- '@vue/reactivity@3.5.38':
+ '@vue/reactivity@3.5.39':
dependencies:
- '@vue/shared': 3.5.38
+ '@vue/shared': 3.5.39
- '@vue/runtime-core@3.5.38':
+ '@vue/runtime-core@3.5.39':
dependencies:
- '@vue/reactivity': 3.5.38
- '@vue/shared': 3.5.38
+ '@vue/reactivity': 3.5.39
+ '@vue/shared': 3.5.39
- '@vue/runtime-dom@3.5.38':
+ '@vue/runtime-dom@3.5.39':
dependencies:
- '@vue/reactivity': 3.5.38
- '@vue/runtime-core': 3.5.38
- '@vue/shared': 3.5.38
+ '@vue/reactivity': 3.5.39
+ '@vue/runtime-core': 3.5.39
+ '@vue/shared': 3.5.39
csstype: 3.2.3
- '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))':
+ '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@5.9.3))':
dependencies:
- '@vue/compiler-ssr': 3.5.38
- '@vue/shared': 3.5.38
- vue: 3.5.38(typescript@5.9.3)
+ '@vue/compiler-ssr': 3.5.39
+ '@vue/shared': 3.5.39
+ vue: 3.5.39(typescript@5.9.3)
+
+ '@vue/shared@3.5.30': {}
'@vue/shared@3.5.38': {}
- '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))':
+ '@vue/shared@3.5.39': {}
+
+ '@vue/tsconfig@0.9.1(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))':
optionalDependencies:
typescript: 5.9.3
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
- '@vueuse/core@14.3.0(vue@3.5.38(typescript@5.9.3))':
+ '@vueuse/core@14.3.0(vue@3.5.39(typescript@5.9.3))':
dependencies:
'@types/web-bluetooth': 0.0.21
'@vueuse/metadata': 14.3.0
- '@vueuse/shared': 14.3.0(vue@3.5.38(typescript@5.9.3))
- vue: 3.5.38(typescript@5.9.3)
+ '@vueuse/shared': 14.3.0(vue@3.5.39(typescript@5.9.3))
+ vue: 3.5.39(typescript@5.9.3)
'@vueuse/metadata@14.3.0': {}
- '@vueuse/shared@14.3.0(vue@3.5.38(typescript@5.9.3))':
+ '@vueuse/shared@14.3.0(vue@3.5.39(typescript@5.9.3))':
dependencies:
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
- acorn@8.17.0: {}
+ acorn@8.16.0: {}
alien-signals@3.2.1: {}
@@ -1662,35 +1505,6 @@ snapshots:
entities@7.0.1: {}
- esbuild@0.25.12:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.25.12
- '@esbuild/android-arm': 0.25.12
- '@esbuild/android-arm64': 0.25.12
- '@esbuild/android-x64': 0.25.12
- '@esbuild/darwin-arm64': 0.25.12
- '@esbuild/darwin-x64': 0.25.12
- '@esbuild/freebsd-arm64': 0.25.12
- '@esbuild/freebsd-x64': 0.25.12
- '@esbuild/linux-arm': 0.25.12
- '@esbuild/linux-arm64': 0.25.12
- '@esbuild/linux-ia32': 0.25.12
- '@esbuild/linux-loong64': 0.25.12
- '@esbuild/linux-mips64el': 0.25.12
- '@esbuild/linux-ppc64': 0.25.12
- '@esbuild/linux-riscv64': 0.25.12
- '@esbuild/linux-s390x': 0.25.12
- '@esbuild/linux-x64': 0.25.12
- '@esbuild/netbsd-arm64': 0.25.12
- '@esbuild/netbsd-x64': 0.25.12
- '@esbuild/openbsd-arm64': 0.25.12
- '@esbuild/openbsd-x64': 0.25.12
- '@esbuild/openharmony-arm64': 0.25.12
- '@esbuild/sunos-x64': 0.25.12
- '@esbuild/win32-arm64': 0.25.12
- '@esbuild/win32-ia32': 0.25.12
- '@esbuild/win32-x64': 0.25.12
-
estree-walker@2.0.2: {}
exsolve@1.0.8: {}
@@ -1765,10 +1579,16 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ local-pkg@1.1.2:
+ dependencies:
+ mlly: 1.8.1
+ pkg-types: 2.3.0
+ quansync: 0.2.11
+
local-pkg@1.2.1:
dependencies:
- mlly: 1.8.2
- pkg-types: 2.3.1
+ mlly: 1.8.1
+ pkg-types: 2.3.0
quansync: 0.2.11
magic-string-ast@1.0.3:
@@ -1787,12 +1607,19 @@ snapshots:
mitt@3.0.1: {}
+ mlly@1.8.1:
+ dependencies:
+ acorn: 8.16.0
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.6.3
+
mlly@1.8.2:
dependencies:
- acorn: 8.17.0
+ acorn: 8.16.0
pathe: 2.0.3
pkg-types: 1.3.1
- ufo: 1.6.4
+ ufo: 1.6.3
muggle-string@0.4.1: {}
@@ -1812,20 +1639,20 @@ snapshots:
picomatch@4.0.4: {}
- pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)):
+ pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)):
dependencies:
'@vue/devtools-api': 7.7.9
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
optionalDependencies:
typescript: 5.9.3
pkg-types@1.3.1:
dependencies:
confbox: 0.1.8
- mlly: 1.8.2
+ mlly: 1.8.1
pathe: 2.0.3
- pkg-types@2.3.1:
+ pkg-types@2.3.0:
dependencies:
confbox: 0.2.4
exsolve: 1.0.8
@@ -1851,13 +1678,13 @@ snapshots:
prettier@3.6.0: {}
- primevue@4.5.5(vue@3.5.38(typescript@5.9.3)):
+ primevue@4.5.5(vue@3.5.39(typescript@5.9.3)):
dependencies:
'@primeuix/styled': 0.7.4
'@primeuix/styles': 2.0.3
'@primeuix/utils': 0.6.4
- '@primevue/core': 4.5.5(vue@3.5.38(typescript@5.9.3))
- '@primevue/icons': 4.5.5(vue@3.5.38(typescript@5.9.3))
+ '@primevue/core': 4.5.5(vue@3.5.39(typescript@5.9.3))
+ '@primevue/icons': 4.5.5(vue@3.5.39(typescript@5.9.3))
transitivePeerDependencies:
- vue
@@ -1867,36 +1694,26 @@ snapshots:
rfdc@1.4.1: {}
- rollup@4.62.0:
+ rolldown@1.0.3:
dependencies:
- '@types/estree': 1.0.9
+ '@oxc-project/types': 0.133.0
+ '@rolldown/pluginutils': 1.0.1
optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.62.0
- '@rollup/rollup-android-arm64': 4.62.0
- '@rollup/rollup-darwin-arm64': 4.62.0
- '@rollup/rollup-darwin-x64': 4.62.0
- '@rollup/rollup-freebsd-arm64': 4.62.0
- '@rollup/rollup-freebsd-x64': 4.62.0
- '@rollup/rollup-linux-arm-gnueabihf': 4.62.0
- '@rollup/rollup-linux-arm-musleabihf': 4.62.0
- '@rollup/rollup-linux-arm64-gnu': 4.62.0
- '@rollup/rollup-linux-arm64-musl': 4.62.0
- '@rollup/rollup-linux-loong64-gnu': 4.62.0
- '@rollup/rollup-linux-loong64-musl': 4.62.0
- '@rollup/rollup-linux-ppc64-gnu': 4.62.0
- '@rollup/rollup-linux-ppc64-musl': 4.62.0
- '@rollup/rollup-linux-riscv64-gnu': 4.62.0
- '@rollup/rollup-linux-riscv64-musl': 4.62.0
- '@rollup/rollup-linux-s390x-gnu': 4.62.0
- '@rollup/rollup-linux-x64-gnu': 4.62.0
- '@rollup/rollup-linux-x64-musl': 4.62.0
- '@rollup/rollup-openbsd-x64': 4.62.0
- '@rollup/rollup-openharmony-arm64': 4.62.0
- '@rollup/rollup-win32-arm64-msvc': 4.62.0
- '@rollup/rollup-win32-ia32-msvc': 4.62.0
- '@rollup/rollup-win32-x64-gnu': 4.62.0
- '@rollup/rollup-win32-x64-msvc': 4.62.0
- fsevents: 2.3.3
+ '@rolldown/binding-android-arm64': 1.0.3
+ '@rolldown/binding-darwin-arm64': 1.0.3
+ '@rolldown/binding-darwin-x64': 1.0.3
+ '@rolldown/binding-freebsd-x64': 1.0.3
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.3
+ '@rolldown/binding-linux-arm64-gnu': 1.0.3
+ '@rolldown/binding-linux-arm64-musl': 1.0.3
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.3
+ '@rolldown/binding-linux-s390x-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-gnu': 1.0.3
+ '@rolldown/binding-linux-x64-musl': 1.0.3
+ '@rolldown/binding-openharmony-arm64': 1.0.3
+ '@rolldown/binding-wasm32-wasi': 1.0.3
+ '@rolldown/binding-win32-arm64-msvc': 1.0.3
+ '@rolldown/binding-win32-x64-msvc': 1.0.3
scule@1.3.0: {}
@@ -1923,9 +1740,12 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tslib@2.8.1:
+ optional: true
+
typescript@5.9.3: {}
- ufo@1.6.4: {}
+ ufo@1.6.3: {}
undici-types@8.3.0: {}
@@ -1934,7 +1754,7 @@ snapshots:
pathe: 2.0.3
picomatch: 4.0.4
- unplugin-vue-components@32.1.0(vue@3.5.38(typescript@5.9.3)):
+ unplugin-vue-components@32.1.0(vue@3.5.39(typescript@5.9.3)):
dependencies:
chokidar: 5.0.0
local-pkg: 1.2.1
@@ -1945,7 +1765,7 @@ snapshots:
tinyglobby: 0.2.17
unplugin: 3.0.0
unplugin-utils: 0.3.1
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
unplugin@3.0.0:
dependencies:
@@ -1957,32 +1777,30 @@ snapshots:
uuid@14.0.0: {}
- vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0):
+ vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0):
dependencies:
- esbuild: 0.25.12
- fdir: 6.5.0(picomatch@4.0.4)
+ lightningcss: 1.32.0
picomatch: 4.0.4
postcss: 8.5.15
- rollup: 4.62.0
+ rolldown: 1.0.3
tinyglobby: 0.2.17
optionalDependencies:
- '@types/node': 26.0.0
+ '@types/node': 26.0.1
fsevents: 2.3.3
jiti: 2.7.0
- lightningcss: 1.32.0
yaml: 2.9.0
vscode-uri@3.1.0: {}
- vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3)))(vite@6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)):
+ vue-router@5.1.0(@vue/compiler-sfc@3.5.39)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3)))(vite@8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0))(vue@3.5.39(typescript@5.9.3)):
dependencies:
'@babel/generator': 8.0.0
- '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@5.9.3))
- '@vue/devtools-api': 8.1.3
+ '@vue-macros/common': 3.1.2(vue@3.5.39(typescript@5.9.3))
+ '@vue/devtools-api': 8.1.5
ast-walker-scope: 0.9.0
chokidar: 5.0.0
json5: 2.2.3
- local-pkg: 1.2.1
+ local-pkg: 1.1.2
magic-string: 0.30.21
mlly: 1.8.2
muggle-string: 0.4.1
@@ -1992,12 +1810,12 @@ snapshots:
tinyglobby: 0.2.17
unplugin: 3.0.0
unplugin-utils: 0.3.1
- vue: 3.5.38(typescript@5.9.3)
+ vue: 3.5.39(typescript@5.9.3)
yaml: 2.9.0
optionalDependencies:
- '@vue/compiler-sfc': 3.5.38
- pinia: 3.0.4(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))
- vite: 6.4.3(@types/node@26.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(yaml@2.9.0)
+ '@vue/compiler-sfc': 3.5.39
+ pinia: 3.0.4(typescript@5.9.3)(vue@3.5.39(typescript@5.9.3))
+ vite: 8.0.16(@types/node@26.0.1)(jiti@2.7.0)(yaml@2.9.0)
vue-tsc@3.3.5(typescript@5.9.3):
dependencies:
@@ -2005,13 +1823,13 @@ snapshots:
'@vue/language-core': 3.3.5
typescript: 5.9.3
- vue@3.5.38(typescript@5.9.3):
+ vue@3.5.39(typescript@5.9.3):
dependencies:
- '@vue/compiler-dom': 3.5.38
- '@vue/compiler-sfc': 3.5.38
- '@vue/runtime-dom': 3.5.38
- '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@5.9.3))
- '@vue/shared': 3.5.38
+ '@vue/compiler-dom': 3.5.39
+ '@vue/compiler-sfc': 3.5.39
+ '@vue/runtime-dom': 3.5.39
+ '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@5.9.3))
+ '@vue/shared': 3.5.39
optionalDependencies:
typescript: 5.9.3