Merge branch 'main' into custom_task_20260812

This commit is contained in:
Ludy
2026-08-21 09:44:49 +02:00
committed by GitHub
15 changed files with 185 additions and 375 deletions
+1 -1
View File
@@ -148,7 +148,7 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up JDK 25
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
java-version: "25"
distribution: "temurin"
@@ -527,27 +527,45 @@
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-image",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-io",
"moduleVersion": "3.13.1",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-io",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-lang",
"moduleVersion": "3.13.1",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.common:common-lang",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-batik",
"moduleVersion": "3.13.1",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-bmp",
"moduleVersion": "3.13.1",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
@@ -557,9 +575,15 @@
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-core",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-jpeg",
"moduleVersion": "3.13.1",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
@@ -569,9 +593,15 @@
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-metadata",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-psd",
"moduleVersion": "3.13.1",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
@@ -581,12 +611,24 @@
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-tiff",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-webp",
"moduleVersion": "3.13.1",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.twelvemonkeys.imageio:imageio-webp",
"moduleVersion": "3.14.0",
"moduleLicense": "The BSD License",
"moduleLicenseUrl": "https://github.com/haraldk/TwelveMonkeys#license"
},
{
"moduleName": "com.vladsch.flexmark:flexmark",
"moduleVersion": "0.64.8",
@@ -2262,7 +2304,7 @@
},
{
"moduleName": "org.simplejavamail:core-module",
"moduleVersion": "9.2.0",
"moduleVersion": "9.3.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
@@ -2275,13 +2317,13 @@
},
{
"moduleName": "org.simplejavamail:outlook-module",
"moduleVersion": "9.2.0",
"moduleVersion": "9.3.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
{
"moduleName": "org.simplejavamail:simple-java-mail",
"moduleVersion": "9.2.0",
"moduleVersion": "9.3.1",
"moduleLicense": "The Apache Software License, Version 2.0",
"moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt"
},
+1 -1
View File
@@ -27,7 +27,7 @@ dependencies {
api 'org.springframework.boot:spring-boot-starter-cache'
api 'com.github.ben-manes.caffeine:caffeine'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.46'
api 'io.swagger.core.v3:swagger-core-jakarta:2.2.53'
implementation "com.bucket4j:bucket4j_jdk17-core:${bucket4jVersion}"
// Lettuce-backed Bucket4j ProxyManager used by ValkeyRateLimitStore for cluster-wide
// token-bucket rate limiting (parity with in-process Bucket4j semantics; no fixed-window
@@ -1,341 +0,0 @@
package stirling.software.proprietary.service.ua;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.service.PdfMetadataService;
import stirling.software.proprietary.pdf.ua.DocumentStructure;
import stirling.software.proprietary.pdf.ua.LayoutAnalyzer;
import stirling.software.proprietary.pdf.ua.PageContent;
import stirling.software.proprietary.pdf.ua.PdfUaProfile;
import stirling.software.proprietary.pdf.ua.PdfUaTagger;
import stirling.software.proprietary.pdf.ua.TaggedContentExtractor;
import stirling.software.proprietary.pdf.ua.TaggingOptions;
/**
* Measures where conversion time and memory go; meant to be read, not to gate CI. Assertions catch
* only order-of-magnitude regressions - wall-clock numbers are no contract.
*/
class PdfUaBenchmarkTest {
private static PdfUaConversionService service;
private static PdfUaValidationService validation;
@BeforeAll
static void setUp() {
validation = new PdfUaValidationService();
validation.initialise();
service =
new PdfUaConversionService(
validation,
new FontEmbeddingService(),
new CustomPDFDocumentFactory(
org.mockito.Mockito.mock(PdfMetadataService.class)));
}
/** A realistic page: heading, prose, a small table, a bullet list. */
private static byte[] document(int pages) throws IOException {
try (PDDocument document = new PDDocument()) {
PDFont font = null;
for (int p = 0; p < pages; p++) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
if (font == null) {
font = PdfUaTestDocuments.font(document);
}
try (PDPageContentStream cs = new PDPageContentStream(document, page)) {
float y = 790;
write(cs, font, 9, 60, 810, "Benchmark Corpus Running Head");
write(cs, font, 18, 60, y, "Section " + (p + 1));
y -= 30;
for (int line = 0; line < 22; line++) {
write(
cs,
font,
11,
60,
y,
"Body line " + line + " of section " + (p + 1) + " with prose.");
y -= 15;
}
for (int row = 0; row < 4; row++) {
cs.beginText();
cs.setFont(font, 11);
cs.newLineAtOffset(60, y);
cs.showText("Row " + row);
cs.newLineAtOffset(160, 0);
cs.showText(String.valueOf(row * 120));
cs.newLineAtOffset(140, 0);
cs.showText(String.valueOf(row * 480));
cs.endText();
y -= 16;
}
write(cs, font, 11, 60, y - 10, "• First bullet point");
write(cs, font, 11, 60, y - 25, "• Second bullet point");
write(cs, font, 9, 300, 30, "Page " + (p + 1));
}
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
document.save(out);
return out.toByteArray();
}
}
private static void write(
PDPageContentStream cs, PDFont font, float size, float x, float y, String text)
throws IOException {
cs.beginText();
cs.setFont(font, size);
cs.newLineAtOffset(x, y);
cs.showText(text);
cs.endText();
}
private static TaggingOptions options() {
return TaggingOptions.builder()
.profile(PdfUaProfile.UA1)
.language("en-GB")
.title("Benchmark")
.embedFonts(false)
.existingTags(TaggingOptions.ExistingTags.REBUILD)
.build();
}
private static long usedHeap() {
Runtime runtime = Runtime.getRuntime();
System.gc();
return runtime.totalMemory() - runtime.freeMemory();
}
@Test
@DisplayName("reports throughput and memory across document sizes")
void throughputAcrossSizes() throws Exception {
int[] sizes = {1, 10, 50, 150};
StringBuilder report =
new StringBuilder("\nPDF/UA conversion throughput\n")
.append(
String.format(
" %-7s %-10s %-12s %-12s %-10s %s%n",
"pages",
"input",
"convert ms",
"ms/page",
"pages/s",
"heap MB"));
// Warm up so the first timed run is not measuring class loading and JIT.
service.convert(document(5), options());
for (int pages : sizes) {
byte[] input = document(pages);
long heapBefore = usedHeap();
long start = System.nanoTime();
var outcome = service.convert(input, options());
long elapsedMs = (System.nanoTime() - start) / 1_000_000;
long heapDelta = (usedHeap() - heapBefore) / (1024 * 1024);
assertTrue(outcome.pdfBytes().length > 0);
report.append(
String.format(
Locale.ROOT,
" %-7d %-10s %-12d %-12.2f %-10.1f %d%n",
pages,
humanBytes(input.length),
elapsedMs,
elapsedMs / (double) pages,
pages * 1000.0 / Math.max(elapsedMs, 1),
Math.max(heapDelta, 0)));
}
System.out.println(report);
}
@Test
@DisplayName("breaks conversion down by phase so optimisation has a target")
void phaseBreakdown() throws Exception {
byte[] input = document(60);
// Warm up.
try (PDDocument warm = Loader.loadPDF(input)) {
new TaggedContentExtractor().extract(warm);
}
long parseMs;
long extractMs;
long analyseMs;
long tagMs;
List<PageContent> pages;
DocumentStructure structure;
long t0 = System.nanoTime();
try (PDDocument document = Loader.loadPDF(input)) {
parseMs = ms(t0);
long t1 = System.nanoTime();
pages = new TaggedContentExtractor().extract(document);
extractMs = ms(t1);
long t2 = System.nanoTime();
structure = new LayoutAnalyzer().analyse(pages);
analyseMs = ms(t2);
}
long t3 = System.nanoTime();
try (PDDocument document = Loader.loadPDF(input)) {
new PdfUaTagger().tag(document, options());
ByteArrayOutputStream out = new ByteArrayOutputStream();
document.save(out);
}
tagMs = ms(t3);
long t4 = System.nanoTime();
var outcome = service.convert(input, options());
long totalMs = ms(t4);
long t5 = System.nanoTime();
validation.validate(outcome.pdfBytes(), PdfUaProfile.UA1);
long validateMs = ms(t5);
System.out.printf(
Locale.ROOT,
"%nPhase breakdown over %d pages (%d blocks)%n"
+ " parse %5d ms%n"
+ " extract %5d ms (text pass + token scan)%n"
+ " analyse %5d ms%n"
+ " tag end-to-end %5d ms (includes parse, extract, analyse, inject, write)%n"
+ " validate %5d ms (veraPDF)%n"
+ " full convert %5d ms (tag + declare + validate)%n",
60,
structure.getBlocks().size(),
parseMs,
extractMs,
analyseMs,
tagMs,
validateMs,
totalMs);
assertTrue(pages.size() == 60, "extractor lost pages");
}
@Test
@DisplayName("splits the tagging pass into its own sub-phases")
void taggingSubPhases() throws Exception {
byte[] input = document(60);
try (PDDocument warm = Loader.loadPDF(input)) {
new TaggedContentExtractor().extract(warm);
}
long extractMs;
long analyseMs;
long injectMs;
long treeMs;
long saveMs;
try (PDDocument document = Loader.loadPDF(input)) {
long t = System.nanoTime();
List<PageContent> pages = new TaggedContentExtractor().extract(document);
extractMs = ms(t);
t = System.nanoTime();
DocumentStructure structure = new LayoutAnalyzer().analyse(pages);
analyseMs = ms(t);
t = System.nanoTime();
var injector = new stirling.software.proprietary.pdf.ua.MarkedContentInjector();
var byPage =
new java.util.LinkedHashMap<
Integer, List<stirling.software.proprietary.pdf.ua.StructBlock>>();
structure
.getBlocks()
.forEach(
b ->
byPage.computeIfAbsent(b.getPageIndex(), k -> new ArrayList<>())
.add(b));
for (int p = 0; p < document.getNumberOfPages(); p++) {
injector.inject(
document, document.getPage(p), byPage.getOrDefault(p, List.of()), 0, true);
}
injectMs = ms(t);
t = System.nanoTime();
new stirling.software.proprietary.pdf.ua.StructTreeWriter()
.write(document, structure, PdfUaProfile.UA1);
treeMs = ms(t);
t = System.nanoTime();
ByteArrayOutputStream out = new ByteArrayOutputStream();
document.save(out);
saveMs = ms(t);
}
System.out.printf(
Locale.ROOT,
"%nTagging sub-phases over 60 pages%n"
+ " extract %5d ms%n"
+ " analyse %5d ms%n"
+ " inject %5d ms%n"
+ " struct tree %5d ms%n"
+ " save %5d ms%n",
extractMs,
analyseMs,
injectMs,
treeMs,
saveMs);
}
@Test
@DisplayName("memory stays proportional to document size, not quadratic")
void memoryScales() throws Exception {
List<String> rows = new ArrayList<>();
long previousPerPage = 0;
boolean blewUp = false;
for (int pages : new int[] {20, 80, 200}) {
byte[] input = document(pages);
long before = usedHeap();
var outcome = service.convert(input, options());
long after = usedHeap();
long perPageKb = Math.max(after - before, 0) / 1024 / pages;
rows.add(
String.format(
Locale.ROOT,
" %-6d pages in %-9s out %-9s ~%d KB/page retained",
pages,
humanBytes(input.length),
humanBytes(outcome.pdfBytes().length),
perPageKb));
// Per-page cost should stay roughly flat; a big jump means something accumulates.
if (previousPerPage > 0 && perPageKb > previousPerPage * 4 && perPageKb > 200) {
blewUp = true;
}
previousPerPage = Math.max(perPageKb, 1);
}
System.out.println("\nMemory scaling\n" + String.join("\n", rows));
assertTrue(!blewUp, "per-page memory grew superlinearly: " + rows);
}
private static long ms(long startNanos) {
return (System.nanoTime() - startNanos) / 1_000_000;
}
private static String humanBytes(int bytes) {
return bytes < 1024 * 1024
? (bytes / 1024) + " KB"
: String.format(Locale.ROOT, "%.1f MB", bytes / 1024.0 / 1024.0);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ ARG BASE_VERSION=1.0.2@sha256:c7698687f486707ddef9e0298587ca8b44c4e96185e1bdb0c3
ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION}
# Stage 1: Build the Java application (backend only, no frontend)
FROM gradle:9.7.0-jdk25@sha256:7d4e63b32991e679b183645680ff81762b6f1ef137850d8c2750b362eb994d08 AS app-build
FROM gradle:9.7.1-jdk25@sha256:a80276ab804c348989df46016e2b5d58cad07c5b29e06f2112434d28ca5b2844 AS app-build
# JDK 25+: --add-exports is no longer accepted via JAVA_TOOL_OPTIONS; use JDK_JAVA_OPTIONS instead
ENV JDK_JAVA_OPTIONS="--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+1 -1
View File
@@ -5,7 +5,7 @@ ARG BASE_VERSION=1.0.2@sha256:c7698687f486707ddef9e0298587ca8b44c4e96185e1bdb0c3
ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION}
# Stage 1: Build the Java application and frontend
FROM gradle:9.7.0-jdk25@sha256:7d4e63b32991e679b183645680ff81762b6f1ef137850d8c2750b362eb994d08 AS app-build
FROM gradle:9.7.1-jdk25@sha256:a80276ab804c348989df46016e2b5d58cad07c5b29e06f2112434d28ca5b2844 AS app-build
ARG TASK_VERSION=3.53.1
RUN apt-get update \
+1 -1
View File
@@ -8,7 +8,7 @@ ARG BASE_VERSION=1.0.2@sha256:c7698687f486707ddef9e0298587ca8b44c4e96185e1bdb0c3
ARG BASE_IMAGE=stirlingtools/stirling-pdf-base:${BASE_VERSION}
# Stage 1: Build the Java application and frontend
FROM gradle:9.7.0-jdk25@sha256:7d4e63b32991e679b183645680ff81762b6f1ef137850d8c2750b362eb994d08 AS app-build
FROM gradle:9.7.1-jdk25@sha256:a80276ab804c348989df46016e2b5d58cad07c5b29e06f2112434d28ca5b2844 AS app-build
ARG TASK_VERSION=3.53.1
RUN apt-get update \
+1 -1
View File
@@ -4,7 +4,7 @@
# Single JAR contains both frontend and backend with minimal dependencies
# Stage 1: Build application with embedded frontend
FROM gradle:9.7.0-jdk25@sha256:7d4e63b32991e679b183645680ff81762b6f1ef137850d8c2750b362eb994d08 AS build
FROM gradle:9.7.1-jdk25@sha256:a80276ab804c348989df46016e2b5d58cad07c5b29e06f2112434d28ca5b2844 AS build
# Install Node.js and npm for frontend build
ARG TASK_VERSION=3.53.1
+1 -1
View File
@@ -49,7 +49,7 @@ tools = [
"deep-translator>=1.11.4",
"defusedxml>=0.7.1",
"fonttools>=4.63.0",
"fpdf2>=2.8.7",
"fpdf2>=2.8.8",
"openai>=2.53.0",
"requests>=2.34.2",
"tomli-w>=1.2.0",
+4 -4
View File
@@ -726,7 +726,7 @@ tools = [
{ name = "deep-translator", specifier = ">=1.11.4" },
{ name = "defusedxml", specifier = ">=0.7.1" },
{ name = "fonttools", specifier = ">=4.63.0" },
{ name = "fpdf2", specifier = ">=2.8.7" },
{ name = "fpdf2", specifier = ">=2.8.8" },
{ name = "openai", specifier = ">=2.53.0" },
{ name = "requests", specifier = ">=2.34.2" },
{ name = "tomli-w", specifier = ">=1.2.0" },
@@ -898,16 +898,16 @@ woff = [
[[package]]
name = "fpdf2"
version = "2.8.7"
version = "2.8.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "defusedxml" },
{ name = "fonttools" },
{ name = "pillow" },
]
sdist = { url = "https://files.pythonhosted.org/packages/27/f2/72feae0b2827ed38013e4307b14f95bf0b3d124adfef4d38a7d57533f7be/fpdf2-2.8.7.tar.gz", hash = "sha256:7060ccee5a9c7ab0a271fb765a36a23639f83ef8996c34e3d46af0a17ede57f9", size = 362351, upload-time = "2026-02-28T05:39:16.456Z" }
sdist = { url = "https://files.pythonhosted.org/packages/1e/bc/8fd4321aed40cadadddc8f311c65b6082346b252bca048f7b476d8f35d72/fpdf2-2.8.8.tar.gz", hash = "sha256:9e94e155e85e8053329a9a1fce8b566fd7a7c5bb79e98a1a3952d379b947c5b9", size = 374689, upload-time = "2026-08-09T23:32:45.334Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/66/0a/cf50ecffa1e3747ed9380a3adfc829259f1f86b3fdbd9e505af789003141/fpdf2-2.8.7-py3-none-any.whl", hash = "sha256:d391fc508a3ce02fc43a577c830cda4fe6f37646f2d143d489839940932fbc19", size = 327056, upload-time = "2026-02-28T05:39:14.619Z" },
{ url = "https://files.pythonhosted.org/packages/f5/be/af012eda9507494f28b99b077423806c43a11573eb6225dd46f19ae2d263/fpdf2-2.8.8-py3-none-any.whl", hash = "sha256:3557a478fc577a929c94aace9666aed4dcc432b5ab6764232e6a59f1ccd75f17", size = 337000, upload-time = "2026-08-09T23:32:43.728Z" },
]
[[package]]
@@ -0,0 +1,79 @@
import { render, screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import { TextInput } from "@app/components/shared/TextInput";
import { MantineProvider } from "@mantine/core";
function renderWithMantine(ui: React.ReactElement) {
return render(<MantineProvider>{ui}</MantineProvider>);
}
describe("TextInput", () => {
it("renders input with value and placeholder", () => {
renderWithMantine(
<TextInput
id="test-input"
name="test-input"
value="hello"
onChange={vi.fn()}
placeholder="Type here..."
/>,
);
const input = screen.getByPlaceholderText("Type here...");
expect(input).toBeInTheDocument();
expect(input).toHaveValue("hello");
});
it("shows clear button when there is a value and triggers clear on click", () => {
const onChange = vi.fn();
renderWithMantine(
<TextInput
id="test-input"
name="test-input"
value="search text"
onChange={onChange}
showClearButton={true}
/>,
);
const clearButton = screen.getByRole("button", {
name: /(?:clear|textInput\.clear)/i,
});
expect(clearButton).toBeInTheDocument();
fireEvent.click(clearButton);
expect(onChange).toHaveBeenCalledWith("");
});
it("does not show clear button when value is empty", () => {
renderWithMantine(
<TextInput
id="test-input"
name="test-input"
value=""
onChange={vi.fn()}
showClearButton={true}
/>,
);
expect(
screen.queryByRole("button", { name: /clear input/i }),
).not.toBeInTheDocument();
});
it("does not show clear button when showClearButton is false", () => {
renderWithMantine(
<TextInput
id="test-input"
name="test-input"
value="some text"
onChange={vi.fn()}
showClearButton={false}
/>,
);
expect(
screen.queryByRole("button", { name: /clear input/i }),
).not.toBeInTheDocument();
});
});
@@ -119,12 +119,25 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(
/>
{shouldShowClearButton && (
<ActionIcon
variant="tertiary"
variant="quiet"
accent="neutral"
shape="circle"
className={styles.clearButton}
style={
{
"--ai-size": "1.25rem",
width: "1.25rem",
height: "1.25rem",
} as React.CSSProperties
}
onClick={handleClear}
aria-label={t("textInput.clear", "Clear input")}
>
<LocalIcon icon="close-rounded" width="1.25rem" height="1.25rem" />
<LocalIcon
icon="close-rounded"
width="0.875rem"
height="0.875rem"
/>
</ActionIcon>
)}
</div>
@@ -209,9 +209,6 @@
}
.super-search-item {
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
padding: 0.45rem 0.5rem;
border: none;
@@ -227,6 +224,18 @@
background: var(--c-hover);
}
.super-search-item .mantine-Button-inner {
width: 100%;
}
.super-search-item .mantine-Button-label {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
overflow: visible;
}
.super-search-item-icon {
display: flex;
align-items: center;
@@ -241,6 +250,7 @@
display: flex;
flex-direction: column;
min-width: 0;
flex: 1 1 auto;
}
.super-search-item-title {
@@ -52,22 +52,29 @@
.clearButton {
position: absolute;
right: 8px;
right: 6px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
padding: 4px;
border-radius: 4px;
display: flex;
--ai-size: 1.35rem !important;
width: 1.35rem !important;
height: 1.35rem !important;
min-width: 1.35rem !important;
min-height: 1.35rem !important;
padding: 0 !important;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: background-color 0.2s ease;
border-radius: var(--radius-full, 9999px);
background: transparent;
border: none;
cursor: pointer;
color: var(--c-text-subtle);
transition:
background-color 0.15s ease,
color 0.15s ease;
}
.clearButton:hover {
background-color: var(--c-hover);
color: var(--c-text);
background-color: var(--c-hover) !important;
}
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500