diff --git a/README.md b/README.md index 405a3ef..2768052 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ GuiInteraction enables standalone Groovy applications to have the same user inte ```groovy dependencies { - implementation 'se.alipsa.gi:gi-swing:0.4.0' // or gi-fx, gi-console + implementation 'se.alipsa.gi:gi-swing:0.4.1' // or gi-fx, gi-console } ``` @@ -40,14 +40,14 @@ dependencies { se.alipsa.gi gi-swing - 0.4.0 + 0.4.1 ``` ### Groovy Script with Grape ```groovy -@Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0') +@Grab('se.alipsa.gi:gi-swing:0.4.1') import se.alipsa.gi.swing.InOut def io = new InOut() @@ -57,14 +57,17 @@ println("File chosen was $file") ## Gade Compatibility -Scripts can run both in Gade and standalone by checking for the `io` variable: +Scripts can run both in Gade and standalone by checking for the `io` variable. +The dependency is loaded only when Gade has not already supplied `io`: ```groovy +import static groovy.grape.Grape.grab + // This makes the code run equally in Gade and in a standalone Groovy script if (!binding.hasVariable('io')) { - @Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0') - import se.alipsa.gi.swing.InOut - binding.setVariable('io', new InOut()) + grab(group: 'se.alipsa.gi', module: 'gi-swing', version: '0.4.1') + def inOutClass = this.class.classLoader.loadClass('se.alipsa.gi.swing.InOut') + binding.setVariable('io', inOutClass.newInstance()) } def file = io.chooseFile("Choose a file", ".", "Pick a file please!") @@ -81,6 +84,9 @@ println("File chosen was $file") ## Building from Source +Install a JDK 21 toolchain before building. Gradle uses that toolchain even if +the Gradle daemon runs on a newer JDK. + ```bash ./gradlew build ``` @@ -109,7 +115,7 @@ refresh needs an API key and can be rate limited; it is not a PR gate. Run ## Requirements -- JDK 21 or later +- Java 21 or later to use the published libraries; JDK 21 to build from source - For gi-fx: JDK with JavaFX support (e.g., BellSoft Liberica Full JDK) ## License diff --git a/build.gradle b/build.gradle index 15ed37e..b7fa17f 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { } group = 'se.alipsa.gi' -version = '0.4.1-SNAPSHOT' +version = '0.4.1' description = 'Allows Gade Gui Interactive capabilities from a standalone app' java { diff --git a/docs/API-Guide.md b/docs/API-Guide.md index 395fefa..6e640bb 100644 --- a/docs/API-Guide.md +++ b/docs/API-Guide.md @@ -16,7 +16,7 @@ This guide provides usage examples for the GuiInteraction library. ```groovy // Using Grape for dependency resolution -@Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0') +@Grab('se.alipsa.gi:gi-swing:0.4.1') import se.alipsa.gi.swing.InOut def io = new InOut() @@ -26,7 +26,7 @@ Or in your `build.gradle`: ```groovy dependencies { - implementation 'se.alipsa.gi:gi-swing:0.4.0' + implementation 'se.alipsa.gi:gi-swing:0.4.1' } ``` @@ -326,14 +326,17 @@ assert FileUtils.baseName("/tmp/report#2.pdf") == "report#2.pdf" ## Gade Compatibility -The library is designed to be compatible with [Gade](https://github.com/Alipsa/gade). Scripts can detect the environment: +The library is designed to be compatible with [Gade](https://github.com/Alipsa/gade). +Scripts can detect the environment and load gi-swing only when `io` is absent: ```groovy +import static groovy.grape.Grape.grab + // Check if running in Gade or standalone if (!binding.hasVariable('io')) { - @Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0') - import se.alipsa.gi.swing.InOut - binding.setVariable('io', new InOut()) + grab(group: 'se.alipsa.gi', module: 'gi-swing', version: '0.4.1') + def inOutClass = this.class.classLoader.loadClass('se.alipsa.gi.swing.InOut') + binding.setVariable('io', inOutClass.newInstance()) } // Now 'io' works the same in both Gade and standalone diff --git a/docs/DEPENDENCIES.md b/docs/DEPENDENCIES.md index a944c41..82984a7 100644 --- a/docs/DEPENDENCIES.md +++ b/docs/DEPENDENCIES.md @@ -18,8 +18,8 @@ The core module includes these key dependencies: ### gi-fx, gi-swing, gi-console UI modules add minimal additional dependencies: -- **gi-fx**: JavaFX (provided by JVM), YearMonthPicker -- **gi-swing**: Batik (SVG), LGoodDatePicker, Swing YearMonthPicker +- **gi-fx**: JavaFX (supplied by the JDK or consumer), fx-yearmonth-picker +- **gi-swing**: swing-widgets (date and year-month pickers); SVG rendering uses Matrix Charts from `gi-common` - **gi-console**: JSoup (HTML parsing) ## Apache Tika Dependency Impact @@ -49,9 +49,9 @@ PDFs or Office documents), add the full parser package: ```groovy dependencies { - implementation 'se.alipsa.gi:gi-common:0.4.0' + implementation 'se.alipsa.gi:gi-common:0.4.1' // Add full Tika parsers for document content extraction - implementation 'org.apache.tika:tika-parsers-standard-package:3.2.3' + implementation 'org.apache.tika:tika-parsers-standard-package:3.3.2' } ``` @@ -64,11 +64,13 @@ This adds support for: ### Fat JAR for Standalone Scripts -The fat JARs (`gi-swing-fatjar`, `gi-fx-fatjar`, etc.) include all dependencies -and are designed for standalone Groovy scripts: +The fat JAR artifacts bundle runtime dependencies but exclude Groovy, which the +consuming application must supply. `gi-fx` also needs JavaFX at runtime, either +from the JDK or as separate dependencies. The artifact classifier is `fatjar`; +disable transitive resolution to avoid loading separate copies of bundled dependencies: ```groovy -@Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0', classifier:'fatjar') +@Grab(group='se.alipsa.gi', module='gi-swing', version='0.4.1', classifier='fatjar', transitive=false) import se.alipsa.gi.swing.InOut ``` diff --git a/docs/PUBLISHING.md b/docs/PUBLISHING.md index 50a82a2..26167b1 100644 --- a/docs/PUBLISHING.md +++ b/docs/PUBLISHING.md @@ -189,7 +189,7 @@ After releasing: - [ ] Push tag: `git push origin vX.Y.Z` - [ ] Create GitHub release: https://github.com/Alipsa/GuiInteraction/releases/new - [ ] Verify on Maven Central (may take 10-30 minutes): https://central.sonatype.com/search?q=se.alipsa.gi -- [ ] Bump version to next SNAPSHOT for development +- [ ] Immediately bump version to the next SNAPSHOT for development before other changes land on main ## Troubleshooting @@ -285,6 +285,9 @@ script generates notes from commit subjects. Both paths are idempotent for a version already in the changelog, so `--skip` retries do not duplicate notes. If a retry finds additional curated notes under `## Unreleased`, the script warns that they are not included in the already-recorded version. +If `release.md` already has a dated heading for the version, the script leaves +that date unchanged. Check it against the actual publication date and re-date +the heading before running a delayed release. Commit curated notes before running `release.sh`: every release path requires `release.md` to be clean. SNAPSHOT and `--bump` releases also require clean diff --git a/gi-common/README.md b/gi-common/README.md index 7e469d0..7159655 100644 --- a/gi-common/README.md +++ b/gi-common/README.md @@ -37,7 +37,7 @@ See the [API Guide](../docs/API-Guide.md) for detailed usage examples. se.alipsa.gi gi-common - 0.4.0 + 0.4.1 ``` diff --git a/gi-console/README.md b/gi-console/README.md index 7142c98..7b0b98c 100644 --- a/gi-console/README.md +++ b/gi-console/README.md @@ -26,7 +26,7 @@ This module provides console-based (text mode) interaction capabilities. It's de ```groovy dependencies { - implementation 'se.alipsa.gi:gi-console:0.4.0' + implementation 'se.alipsa.gi:gi-console:0.4.1' } ``` @@ -36,14 +36,14 @@ dependencies { se.alipsa.gi gi-console - 0.4.0 + 0.4.1 ``` ### Grape (Groovy Script) ```groovy -@Grab(group:'se.alipsa.gi', module:'gi-console', version:'0.4.0') +@Grab('se.alipsa.gi:gi-console:0.4.1') import se.alipsa.gi.console.InOut def io = new InOut() @@ -76,11 +76,11 @@ io.view("

Hello

Welcome!

", "Greeting") ## Fat JAR -A fat JAR (with all dependencies bundled) is available: +A fat JAR bundles runtime dependencies; the consuming application supplies Groovy: ```groovy dependencies { - implementation 'se.alipsa.gi:gi-console:0.4.0:fatjar' + implementation 'se.alipsa.gi:gi-console:0.4.1:fatjar' } ``` @@ -88,7 +88,7 @@ dependencies { - **Clipboard**: Clipboard access is unavailable in headless environments; clipboard methods log the issue and return `null` or no-op. - **Password Input**: Uses masked input through `System.console()` when available. In IDEs and some CI environments, it falls back to visible stdin input and logs a warning. -- **Charts/Images**: `display(Chart)` and `display(File)` for images print a message instead of showing graphics +- **Charts/Images**: chart display prints a message instead of showing graphics; `display(File)` asks the desktop application to open an existing file when the platform supports it - **Swing Components**: `display(JComponent)` is not supported - **File Choosers**: User manually types file paths instead of browsing diff --git a/gi-fx/README.md b/gi-fx/README.md index cd7fc40..fb35b79 100644 --- a/gi-fx/README.md +++ b/gi-fx/README.md @@ -13,7 +13,7 @@ This module provides JavaFX-based dialogs and viewers for user interaction. It o ## Features -- Native JavaFX dialogs for file/directory selection +- JavaFX dialogs for file/directory selection - Rich date and year-month pickers with calendar UI - WebView-based HTML rendering - Markdown viewing with full formatting support @@ -26,7 +26,7 @@ This module provides JavaFX-based dialogs and viewers for user interaction. It o ```groovy dependencies { - implementation 'se.alipsa.gi:gi-fx:0.4.0' + implementation 'se.alipsa.gi:gi-fx:0.4.1' } ``` @@ -36,14 +36,14 @@ dependencies { se.alipsa.gi gi-fx - 0.4.0 + 0.4.1 ``` ### Grape (Groovy Script) ```groovy -@Grab(group:'se.alipsa.gi', module:'gi-fx', version:'0.4.0') +@Grab('se.alipsa.gi:gi-fx:0.4.1') import se.alipsa.gi.fx.InOut def io = new InOut() @@ -71,11 +71,11 @@ io.viewMarkdown("# Title\n\nSome **bold** text", "Document") ## Fat JAR -A fat JAR (with all dependencies bundled) is available: +A fat JAR bundles runtime dependencies; the consuming application supplies Groovy and JavaFX: ```groovy dependencies { - implementation 'se.alipsa.gi:gi-fx:0.4.0:fatjar' + implementation 'se.alipsa.gi:gi-fx:0.4.1:fatjar' } ``` diff --git a/gi-swing/README.md b/gi-swing/README.md index 44edd58..9564eac 100644 --- a/gi-swing/README.md +++ b/gi-swing/README.md @@ -13,7 +13,7 @@ This module provides Swing-based dialogs and viewers for user interaction. It wo ## Features - Standard Swing file and directory choosers -- Date and year-month pickers (via LGoodDatePicker) +- Date and year-month pickers via `se.alipsa:swing-widgets` - HTML viewing via JEditorPane - Table display with JTable - Clipboard operations @@ -26,7 +26,7 @@ This module provides Swing-based dialogs and viewers for user interaction. It wo ```groovy dependencies { - implementation 'se.alipsa.gi:gi-swing:0.4.0' + implementation 'se.alipsa.gi:gi-swing:0.4.1' } ``` @@ -36,14 +36,14 @@ dependencies { se.alipsa.gi gi-swing - 0.4.0 + 0.4.1 ``` ### Grape (Groovy Script) ```groovy -@Grab(group:'se.alipsa.gi', module:'gi-swing', version:'0.4.0') +@Grab('se.alipsa.gi:gi-swing:0.4.1') import se.alipsa.gi.swing.InOut def io = new InOut() @@ -75,11 +75,11 @@ io.view([ ## Fat JAR -A fat JAR (with all dependencies bundled) is available: +A fat JAR bundles runtime dependencies; the consuming application supplies Groovy: ```groovy dependencies { - implementation 'se.alipsa.gi:gi-swing:0.4.0:fatjar' + implementation 'se.alipsa.gi:gi-swing:0.4.1:fatjar' } ``` @@ -89,8 +89,7 @@ dependencies { ## Dependencies -- [LGoodDatePicker](https://github.com/LGoodDatePicker/LGoodDatePicker) - Date picker component -- [swing-yearmonth-picker](https://github.com/Alipsa/swing-yearmonth-picker) - Year-month picker +- `se.alipsa:swing-widgets` - Date and year-month picker components ## API Documentation diff --git a/gi-swing/test.gsh b/gi-swing/test.gsh index fe82c63..4c4406c 100644 --- a/gi-swing/test.gsh +++ b/gi-swing/test.gsh @@ -2,8 +2,8 @@ import static groovy.grape.Grape.grab if (! binding.hasVariable('io')) { grab(group:"se.alipsa.gi", module: "gi-swing", version:"0.4.0") - binding.setVariable('io', this.class.classLoader.loadClass("se.alipsa.gi.swing.InOut").getDeclaredConstructor().newInstance()) + binding.setVariable('io', this.class.classLoader.loadClass("se.alipsa.gi.swing.InOut").newInstance()) } def file = io.chooseFile("Choose a file", ".", "Pick a file please!" ) -println("File chosen was $file") \ No newline at end of file +println("File chosen was $file") diff --git a/release.md b/release.md index 8d2e37f..d23d5cd 100644 --- a/release.md +++ b/release.md @@ -1,14 +1,17 @@ # Gui Interaction Release Notes ## Unreleased + +## 0.4.1 - 2026-09-27 - Fixed (build): pin Java 21 toolchains in all modules; run Spotless and SpotBugs in default builds; scan all modules with `dependencyCheckAggregate`. -- Fixed (build): select JavaFX natives for the Gradle JVM's OS and architecture; unsupported combinations fail when JavaFX dependencies resolve, leaving unrelated tasks available. -- Fixed: console null file names and password EOF no longer throw; null matrix views are guarded and unnamed JavaFX tables use `Table` as their title. +- Fixed (build): select JavaFX natives for the host OS and Java 21 toolchain architecture; unsupported combinations fail when JavaFX dependencies resolve, leaving unrelated tasks available. +- Fixed: console null file names and password EOF no longer throw; null matrix views are guarded and unnamed Swing and JavaFX tables use `Table` as their title. - Fixed: JavaFX copy shortcut detection uses locale-independent casing, and the redundant `rowsForViewer` helper was removed. - Fixed (packaging): merge duplicate licence, notice, and dependency metadata in fat JARs. - Fixed (release): promote curated Unreleased notes, preserve backslashes in commit subjects, handle release retries, and validate version shapes before mutating files. -- Added: debug diagnostics for `urlExists` failures and Windows shell branch coverage with a CI smoke job. -- Build: upgraded Groovy to 5.1.3, Matrix BOM to 2.6.0, CommonMark to 0.30.0, jsoup to 1.23.2, JUnit to 6.1.3, Gradle Versions to 0.64.0, Spotless to 8.10.2, and SpotBugs (plugin/engine) to 6.5.11/4.10.4. +- Added: `urlExists` warnings for unsupported schemes and broken redirects, with debug diagnostics for connection and HTTP failures. Logged URLs redact credentials, including malformed URL paths. +- Added (test): Windows shell execution integration coverage. +- Build: upgraded Groovy to 5.1.3, Matrix BOM to 2.6.0, CommonMark to 0.30.0, jsoup to 1.23.2, JUnit to 6.1.3, Gradle Versions to 0.64.0, Spotless to 8.10.3, and SpotBugs (plugin/engine) to 6.5.12/4.10.4. - Common: added `sh` and `shell` methods for executing platform shell commands. `sh` returns captured standard output and can stream output while the command runs; `shell` returns a `ShellResult` containing standard output, standard error, exit code, and success status. Both support optional timeouts. These methods execute arbitrary commands and should not receive untrusted input. - Breaking (packaging): fat JARs are now built with GradleUp Shadow via the `shadowJar` task instead of a hand-rolled `fatJar` task. The published artifact name is unchanged (`--fatjar.jar`), but the Gradle task is now `shadowJar`. - Fixed: fat JARs dropped all but the first copy of each `META-INF/services` entry, disabling most ph-css/gsvg service registrations and part of Tika's. Service files are now merged. @@ -25,10 +28,12 @@ - Fixed: console `display(File)` called `Desktop.open` without checking that the OPEN action is supported, and threw on a null file. Both are now reported on stdout. - Fixed: console input now honors `stdin.encoding` (JDK 25+) or the platform console's charset (JDK 17+) instead of always using the JVM default. - Fixed: `saveToClipboard(File)` wrote `Lost ownership` to stdout when clipboard ownership changed. -- Build: CI now runs `./gradlew check --no-configuration-cache`, which is the only invocation under which Spotless and SpotBugs are applied. The scheduled dependency-check job is reachable again. +- Build: CI builds and tests all modules on Linux and Windows, including Spotless and SpotBugs. The dependency-check job runs on a schedule or by manual dispatch. - Build: `release.sh` verifies the version was actually written to `build.gradle`, names the modules already published when a release fails partway, validates/explicitly confirms `--skip ` before resuming without re-publishing them, and rejects `--skip` with `--bump`. - Fixed: publishing `gi-console`, `gi-fx`, or `gi-swing` failed with "multiple artifacts with the identical extension and classifier" because the Shadow migration both declared the fat-jar artifact explicitly and let Shadow's own publication variant add it. The explicit declaration is removed; Shadow's variant is sufficient. - Fixed: fat jars now also merge `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`, so bundling multiple Groovy extension-module jars (e.g. `matrix-core` and `matrix-groovy-ext`) no longer silently hides one's extension methods behind the other's. +- Fixed (release): choose the newest installed JavaFX-capable SDKMAN JDK 21 when several versions are available. +- Fixed (test): load the Swing HTML page test from a stable fixture so Windows can complete it without a temporary-file deletion race. ## 0.4.0 - 2026-08-03 - Breaking: `FileUtils.getResourceUrl` returns `null` for paths that do not exist; use `File` for output targets that will be created later.