Skip to content

cheroliv/slider-gradle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

279 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Gradle Slider Project

Description

This project demonstrates the use of the com.cheroliv.slider plugin with Gradle 9.4.0 and Java 25 to create interactive presentations using AsciidoctorJ Reveal.js.

All slide generation logic is fully encapsulated in the slider-plugin/, keeping the consumer build script minimal.

AI-assisted generation relies on a two-step RAG pipeline:
proposeDeckContext prepares a pedagogical context file reviewed by the author,
generateDeck produces the final AsciiDoc deck enriched by project examples.

Use Cases

@startuml
left to right direction

actor "Developer" as Dev
actor "AI Provider\n(Ollama, Gemini, Mistral, HF)" as AI
actor "pgvector\n(Docker)" as PG
actor "Git Repository" as Git

rectangle "Gradle Slider Plugin" {
  Dev --> (Initialize slides project)
  Dev --> (Propose a deck context)
  Dev --> (Generate a deck)
  Dev --> (Reindex RAG store)
  Dev --> (Compile slides)
  Dev --> (Serve slides locally)
  Dev --> (Publish slides)

  (Propose a deck context) --> PG
  (Propose a deck context) --> AI
  (Generate a deck) --> PG
  (Generate a deck) --> AI
  (Reindex RAG store) --> PG
  (Publish slides) --> Git
}
@enduml

Global Flow Overview

@startuml
start
:Gradle starts;
:Plugin applied;
:Slides project initialization;

if (proposeDeckContext ?) then (yes)
  :PgVectorService starts Docker;
  :RAG β€” retrieve relevant chunks;
  :LLM β€” propose DeckContext JSON;
  :Write <slug>-deck-context.yml;
  :PgVectorService stopped;
endif

note right
  Human review
  of deck-context.yml
end note

if (generateDeck ?) then (yes)
  :PgVectorService starts Docker;
  :RAG β€” AsciiDoc syntax examples;
  :LLM β€” generate full AsciiDoc;
  :Write <slug>_<lang>-deck.adoc;
  :PgVectorService stopped;
endif

if (build slides ?) then (yes)
  :Asciidoctor RevealJS;
endif

if (serve ?) then (yes)
  :npx serve;
endif

if (publish ?) then (yes)
  :Git push;
endif
stop
@enduml

Current version: 0.0.5

Prerequisites

  • JDK 25 (tested with Eclipse Temurin 25.0.2), 23+ supported

  • Gradle Wrapper (version 9.4.0 included)

  • Docker (for the pgvector container used by the RAG pipeline)

  • Node.js / npx (for the serveSlides task)

  • Internet connection (to download Reveal.js dependencies)

Minimal Consumer Configuration

settings.gradle.kts

pluginManagement.repositories.gradlePluginPortal()

rootProject.name = "slider-gradle"

build.gradle.kts

plugins { alias(libs.plugins.slider) }

slider { configPath = file("slides-context.yml").absolutePath }

gradle/libs.versions.toml

[versions]
slider = "0.0.5"

[plugins]
slider = { id = "com.cheroliv.slider", version.ref = "slider" }

Project Structure

.
β”œβ”€β”€ build.gradle.kts          # Main Gradle configuration (consumer)
β”œβ”€β”€ settings.gradle.kts       # pluginManagement configuration
β”œβ”€β”€ gradle/
β”‚   β”œβ”€β”€ libs.versions.toml    # Dependency catalog
β”‚   └── wrapper/              # Gradle Wrapper 9.4.0
β”œβ”€β”€ slides/
β”‚   └── misc/                 # AsciiDoc presentation sources
β”‚       β”œβ”€β”€ *_<lang>-deck.adoc          # Generated decks (e.g. kotlin-intro_fr-deck.adoc)
β”‚       β”œβ”€β”€ *-deck-context.yml          # AI generation contexts (no language code)
β”‚       β”œβ”€β”€ index.html                  # Presentation dashboard
β”‚       └── images/                     # Image resources
β”œβ”€β”€ slides-context.yml        # Git push config + AI API keys
β”œβ”€β”€ slider-plugin/            # Gradle plugin (plugin source)
β”œβ”€β”€ README.adoc               # This file
└── README_fr.adoc            # French version

Global Architecture

@startuml

package "Consumer Gradle Project" {
  [build.gradle.kts]
  [slides-context.yml]
  [slides/]
}

package "Slider Gradle Plugin" {
  [SliderPlugin]
  [SliderExtension]
  [SlidesInitializer]
  [AssistantManager]
  [RagManager]
  [PgVectorService]
  [SlidesBuilder]
  [SlidesPublisher]
  [SlidesServer]
}

package "External Systems" {
  [AsciidoctorJ Reveal.js]
  [Node npx serve]
  [Git]
  [pgvector\n(Docker)]
  [AI Providers\n(Ollama / Gemini / Mistral / HF)]
}

[build.gradle.kts] --> [SliderPlugin]
[SliderPlugin] --> [SliderExtension]
[SliderPlugin] --> [SlidesInitializer]
[SliderPlugin] --> [AssistantManager]
[SliderPlugin] --> [SlidesBuilder]
[SliderPlugin] --> [SlidesPublisher]
[SliderPlugin] --> [SlidesServer]
[AssistantManager] --> [RagManager]
[AssistantManager] --> [PgVectorService]
[RagManager] --> [pgvector\n(Docker)]
[PgVectorService] --> [pgvector\n(Docker)]
[AssistantManager] --> [AI Providers\n(Ollama / Gemini / Mistral / HF)]
[SlidesBuilder] --> [AsciidoctorJ Reveal.js]
[SlidesServer] --> [Node npx serve]
[SlidesPublisher] --> [Git]
@enduml

Gradle Execution Cycle

@startuml
actor Developer

participant "Gradle" as Gradle
participant "SliderPlugin"
participant "SlidesInitializer"
participant "PgVectorService\n(BuildService)"
participant "RagManager"
participant "AssistantManager"
participant "Tasks"

Developer -> Gradle : ./gradlew <task>
Gradle -> SliderPlugin : apply()
SliderPlugin -> SlidesInitializer : checkStructure()
SlidesInitializer --> SliderPlugin : structure OK / resources generated
SliderPlugin -> Tasks : register tasks
Gradle -> Tasks : execute task

opt RAG task (proposeDeckContext / generateDeck / reindexRag)
  Tasks -> PgVectorService : start()
  PgVectorService --> Tasks : assigned port
  Tasks -> RagManager : retrieve() / reindex()
  RagManager -> Tasks : RAG chunks
  Tasks -> AssistantManager : resolveModel(provider)
  AssistantManager --> Tasks : ChatModel
  Tasks --> Gradle : result
  Gradle -> PgVectorService : close()
end
@enduml

Automatic Initialization (first use)

On the first execution of any Gradle task, the plugin checks whether the slides/ folder,
the slides-context.yml file and slides/misc/example-deck-context.yml are present and complete.

@startuml
start
:Gradle task execution;

if (slides/ exists ?) then (yes)
  if (*-deck.adoc present ?) then (yes)
    :continue;
  else (no)
    :extract slides template;
  endif
else (no)
  :extract slides.zip from classpath;
endif

if (slides-context.yml exists ?) then (yes)
  :load configuration;
else (no)
  :generate default SlidesConfiguration;
endif

if (example-deck-context.yml exists ?) then (yes)
  :continue;
else (no)
  :generate deck-context template;
endif
stop
@enduml

Initializing slides/

A slides/ folder is considered complete if slides/misc/ contains:

  • index.html β€” presentation dashboard

  • at least one *-deck.adoc source file

If any of these conditions is not met, the plugin automatically extracts a default slides/
folder from the zip bundled in its classpath.

Note
deck.properties has been removed β€” decks are discovered by scanning *-deck.adoc
files directly in slides/misc/, in line with the <slug>_<lang>-deck.adoc convention.

Initializing slides-context.yml

If slides-context.yml is missing, the plugin automatically generates one from the
typed SlidesConfiguration model. This file contains the Git push configuration and
AI provider API keys:

srcPath: docs/asciidocRevealJs
pushSlides:
  from: build/docs/asciidocRevealJs
  to: build/slides-repo
  branch: main
  message: deploy slides
  repo:
    name: slides
    repository: https://github.com/your-org/your-slides-repo.git
    credentials:
      username: your-username
      password: your-token
ai:
  gemini:
    - your-gemini-api-key
  mistral:
    - your-mistral-api-key
  huggingface:
    - your-huggingface-api-key
Note
slides-context.yml contains sensitive credentials β€” add it to .gitignore.

Initializing example-deck-context.yml

If slides/misc/example-deck-context.yml is missing, the plugin generates a ready-to-use template:

subject: "Your presentation subject"
audience: "Your target audience"
duration: 45
language: "en"
outputFile: "example_en-deck.adoc"
author:
  name: "Your Name"
  email: "your.email@example.com"
revealjs:
  theme: "sky"
  slideNumber: "c/t"
  width: 1408
  height: 792
notes:
  speakerNotes: true
  pageNotes: true
  pageNotesStyle: "DETAILED"
slides:
  - title: "Agenda"
    speakerHint: "Present the outline in 2 minutes, ask what the audience already knows."
    pageNotesHint: "List prerequisites and suggested readings."
Note
If all three resources already exist, the plugin never modifies existing content.

Plugin DSL Configuration

slider {
    // Path to the YAML configuration file (required)
    configPath = file("slides-context.yml").absolutePath
}

Slider Tasks

Build

asciidoctorRevealJs

Compiles .adoc sources into an HTML Reveal.js presentation.
Slides are generated in build/docs/asciidocRevealJs/.

./gradlew asciidoctorRevealJs
asciidoctor

Runs standard Asciidoctor conversion (depends on asciidoctorRevealJs).

cleanSlidesBuild

Deletes generated presentation artifacts from the build directory.

./gradlew cleanSlidesBuild
dashSlidesBuild

Generates index.html and slides.json listing all available presentations.

Serve

serveSlides

Serves slides via the serve package run by npx.
Ideal for quick local preview.

./gradlew serveSlides

Capsule

asciidocCapsule

(TODO) β€” Video capsule generation from slides. Currently logs a placeholder message.

./gradlew asciidocCapsule

Deploy

publishSlides

Deploys generated slides to the remote repository configured in slides-context.yml.

./gradlew publishSlides

Deployment Pipeline

@startuml
actor Developer

participant Gradle
participant publishSlides
participant "Local build"
participant Git

Developer -> Gradle : ./gradlew publishSlides
Gradle -> publishSlides
publishSlides -> "Local build" : collect compiled slides
publishSlides -> Git : clone remote repository
publishSlides -> Git : copy slides
publishSlides -> Git : commit
publishSlides -> Git : push (force)
@enduml

AI-Assisted Deck Generation (slider-ai)

The plugin integrates a RAG (Retrieval-Augmented Generation) pipeline to generate
complete AsciiDoc/Reveal.js decks. A pgvector store launched via Docker indexes the
project’s AsciiDoc sources and enriches each LLM call with relevant examples.

Four providers are available: ollama (default), gemini, mistral and huggingface.
Select the provider with -Pai.provider.

RAG Pipeline Architecture

@startuml
skinparam componentStyle uml2

package "Gradle Slider Plugin" {
  component "ProposeDeckContextTask" as ProposeTask
  component "GenerateDeckTask" as GenerateTask
  component "ReindexRagTask" as ReindexTask
  component "AssistantManager" as AM
  component "RagManager" as RM
  component "PgVectorService\n(BuildService)" as PGS
  component "PromptManager" as PM
}

package "Infrastructure" {
  component "pgvector\n(Docker)" as PG
  component "AllMiniLmL6V2\n(ONNX in-process)" as Embed
}

package "LLM Providers" {
  component "Ollama"
  component "Gemini"
  component "Mistral"
  component "HuggingFace"
}

component "*-deck-context.yml" as CtxFile
component "<slug>_<lang>-deck.adoc" as DeckFile

ProposeTask --> PGS : start()
ProposeTask --> RM : retrieve(subject)
ProposeTask --> AM : resolveModel(provider)
ProposeTask --> PM : contextSystemPrompt / contextUserMessage
ProposeTask --> CtxFile : write

GenerateTask --> PGS : start()
GenerateTask --> RM : retrieve(AsciiDoc syntax)
GenerateTask --> AM : resolveModel(provider)
GenerateTask --> PM : deckSystemPrompt / deckUserMessage
GenerateTask --> DeckFile : write

ReindexTask --> PGS : start()
ReindexTask --> RM : reindex()

RM --> Embed : embedAll()
RM --> PG : search() / addAll() / removeAll()
PGS --> PG : docker start/stop

AM --> Ollama
AM --> Gemini
AM --> Mistral
AM --> HuggingFace
@enduml

Two-Step Generation Pipeline

@startuml
actor Developer

participant "Gradle\n(proposeDeckContext)" as Propose
participant "PgVectorService" as PGS
participant "RagManager" as RAG
participant "LLM Provider" as LLM
collections "*-deck-context.yml" as CtxFile

participant "Gradle\n(generateDeck)" as Generate
collections "<slug>_<lang>-deck.adoc" as DeckFile

== Step 1: proposeDeckContext ==

Developer -> Propose : ./gradlew proposeDeckContext\n  -Psubject="..." -Planguage=en\n  -Pai.provider=gemini --no-daemon
Propose -> PGS : start()
activate PGS
Propose -> RAG : retrieve(subject)
RAG --> Propose : RAG chunks
Propose -> LLM : contextSystemPrompt + userMessage
LLM --> Propose : DeckContext JSON
Propose -> CtxFile : write <slug>-deck-context.yml
Propose -> PGS : close() [end of build]
deactivate PGS

note over Developer : Review deck-context.yml

== Step 2: generateDeck ==

Developer -> Generate : ./gradlew generateDeck\n  -Pdeck.context=slides/misc/<slug>-deck-context.yml\n  -Pai.provider=gemini --no-daemon
Generate -> PGS : start()
activate PGS
Generate -> RAG : retrieve("AsciiDoc Reveal.js syntax <subject>")
RAG --> Generate : AsciiDoc syntax examples
Generate -> LLM : deckSystemPrompt + userMessage
LLM --> Generate : AsciiDoc content
Generate -> DeckFile : write <slug>_en-deck.adoc
Generate -> PGS : close() [end of build]
deactivate PGS
@enduml

Available AI Tasks

reindexRag

Drops and fully rebuilds the pgvector index from project sources.
Run after adding or deleting source files.

./gradlew reindexRag --no-daemon

proposeDeckContext

Proposes a *-deck-context.yml for a given subject, enriched by RAG context.
Author is resolved automatically from git config user.name / git config user.email.

# Default provider: ollama (local)
./gradlew proposeDeckContext \
  -Psubject="Kotlin Coroutines" \
  -Planguage=en \
  --no-daemon

# Gemini
./gradlew proposeDeckContext \
  -Psubject="Kotlin inline functions and reification" \
  -Planguage=fr \
  -Pai.provider=gemini \
  --no-daemon

# Override author
./gradlew proposeDeckContext \
  -Psubject="Spring Boot 3" \
  -Planguage=en \
  -Pai.provider=mistral \
  -Pauthor.name="cheroliv" \
  -Pauthor.email="cheroliv@example.com" \
  --no-daemon

Output file: slides/misc/<slug>-deck-context.yml.
Review and adjust before running generateDeck.

generateDeck

Reads a validated *-deck-context.yml and generates the complete AsciiDoc deck,
enriched with AsciiDoc syntax examples retrieved from the RAG store.

# Gemini
./gradlew generateDeck \
  -Pdeck.context=slides/misc/kotlin-inline-functions-and-reification-deck-context.yml \
  -Pai.provider=gemini \
  --no-daemon

# Mistral AI
./gradlew generateDeck \
  -Pdeck.context=slides/misc/kotlin-coroutines-deck-context.yml \
  -Pai.provider=mistral \
  --no-daemon

Output file: slides/misc/<slug>_<lang>-deck.adoc
where <lang> is the ISO 639-1 code from the language field in deck-context.yml.

Note
Always use --no-daemon for RAG tasks. The Gradle daemon reuses the JVM process
between builds, preventing native ONNX library reload (libtokenizers.so) and causing
an UnsatisfiedLinkError on the second build.

File Naming Convention

File Pattern Example

Generation context

<slug>-deck-context.yml

kotlin-inline-functions-and-reification-deck-context.yml

Generated AsciiDoc deck

<slug>_<lang>-deck.adoc

kotlin-inline-functions-and-reification_fr-deck.adoc

Compiled HTML deck

<slug>_<lang>-deck.html

kotlin-inline-functions-and-reification_fr-deck.html

The <slug> is derived from the subject in kebab-case (accents normalized, special characters replaced by -).
The <lang> is the ISO 639-1 code passed via -Planguage (e.g. fr, en, de).

Provider Selection

-Pai.provider Default model API key in slides-context.yml

ollama (default)

smollm:135m (local)

none β€” local inference

gemini

gemini-2.5-flash

ai.gemini[0]

mistral

mistral-small-latest

ai.mistral[0]

huggingface

Llama-3.1-8B-Instruct:sambanova

ai.huggingface[0]

If -Pai.provider is absent or set to an unknown value, the task falls back to ollama and logs a warning.

deck-context.yml Format

subject: "Kotlin inline functions and reification"
audience: "intermediate Kotlin developers"
duration: 60
language: "en"
outputFile: "kotlin-inline-functions-and-reification_en-deck.adoc"
author:
  name: "cheroliv"
  email: "cheroliv@example.com"
revealjs:
  theme: "sky"
  slideNumber: "c/t"
  width: 1408
  height: 792
notes:
  speakerNotes: true    # generates [NOTE.speaker] on every slide
  pageNotes: true       # generates [.notes] on every slide
  pageNotesStyle: "DETAILED"  # MINIMAL | DETAILED | EXERCISES_ONLY
slides:
  - title: "Why inline?"
    speakerHint: "Start from the JVM cost of lambdas β€” object allocation on each call."
    pageNotesHint: "JMH benchmarks: inline vs non-inline on a 100k-element collection."
  - title: "reified: access the type at runtime"
    speakerHint: "Show the type-erasure error, then the reified solution."
    pageNotesHint: "Exercise: re-implement a typed version of Gson.fromJson() using reified."

slides is optional β€” if empty, the LLM decides the slide structure freely.

Notes Styles

Style Content generated in [.notes]

MINIMAL

One reference line only

DETAILED

Deep content + references + exercises

EXERCISES_ONLY

Practical exercises only

Typical Workflows

Full pipeline: propose β†’ review β†’ generate β†’ compile β†’ serve
# Step 1 β€” propose the context
./gradlew proposeDeckContext \
  -Psubject="Kotlin inline functions and reification" \
  -Planguage=en \
  -Pai.provider=gemini \
  --no-daemon

# β†’ review slides/misc/kotlin-inline-functions-and-reification-deck-context.yml

# Step 2 β€” generate the deck
./gradlew generateDeck \
  -Pdeck.context=slides/misc/kotlin-inline-functions-and-reification-deck-context.yml \
  -Pai.provider=gemini \
  --no-daemon

# Step 3 β€” compile and serve
./gradlew asciidoctorRevealJs serveSlides
Build and preview locally
./gradlew serveSlides
Clean and rebuild
./gradlew cleanSlidesBuild asciidoctorRevealJs
Publish to remote repository
./gradlew asciidoctorRevealJs publishSlides

Advanced Architecture

C4 View β€” Plugin Context

@startuml
actor Developer

rectangle "Developer Environment" {
  rectangle "Gradle Build System" {
    component "Slider Gradle Plugin"
  }
}

rectangle "Slider Internal Components" {
  component "Slides Initializer"
  component "AssistantManager"
  component "RagManager"
  component "PgVectorService"
  component "Slides Builder"
  component "Slides Publisher"
}

rectangle "RAG Pipeline" {
  component "AllMiniLmL6V2\n(ONNX in-process)"
  component "pgvector\n(Docker)"
  component "PromptManager"
}

rectangle "External Systems" {
  component "LLM Providers\n(Ollama / Gemini / HF / Mistral)"
  component "Reveal.js / Asciidoctor"
  component "Git Repository"
}

Developer --> "Gradle Build System"
"Gradle Build System" --> "Slider Gradle Plugin"

"Slider Gradle Plugin" --> "Slides Initializer"
"Slider Gradle Plugin" --> "AssistantManager"
"Slider Gradle Plugin" --> "Slides Builder"
"Slider Gradle Plugin" --> "Slides Publisher"

"AssistantManager" --> "RagManager"
"AssistantManager" --> "PgVectorService"
"AssistantManager" --> "PromptManager"
"AssistantManager" --> "LLM Providers\n(Ollama / Gemini / HF / Mistral)"

"RagManager" --> "AllMiniLmL6V2\n(ONNX in-process)"
"RagManager" --> "pgvector\n(Docker)"
"PgVectorService" --> "pgvector\n(Docker)"

"Slides Builder" --> "Reveal.js / Asciidoctor"
"Slides Publisher" --> "Git Repository"
@enduml

Hexagonal Architecture (Ports & Adapters)

The plugin follows a hexagonal architecture that separates business logic,
external interfaces and infrastructure technologies.
This design guarantees LLM provider independence and high testability.

@startuml
skinparam componentStyle rectangle

package "Domain Core" {
  component "Deck Generation Logic"
  component "RAG Retrieval"
  component "Presentation Context\n(DeckContext)"
}

package "Application Layer" {
  component "AssistantManager"
  component "RagManager"
  component "SlidesInitializer"
  component "SlidesPublisher"
}

package "Ports" {
  interface "LLM Port"
  interface "Embedding Store Port"
  interface "Docker Service Port"
  interface "Slides Renderer Port"
  interface "Git Repository Port"
}

package "Adapters" {
  component "Ollama Adapter"
  component "Gemini Adapter"
  component "Mistral Adapter"
  component "HuggingFace Adapter"
  component "PgVectorEmbeddingStore"
  component "PgVectorService\n(docker-java)"
  component "RevealJS Adapter"
  component "Git Adapter"
  component "Gradle Task Adapter\n(RagTask)"
}

"AssistantManager" --> "LLM Port"
"RagManager" --> "Embedding Store Port"
"RagManager" --> "Docker Service Port"
"SlidesPublisher" --> "Git Repository Port"
"SlidesInitializer" --> "Slides Renderer Port"

"LLM Port" --> "Ollama Adapter"
"LLM Port" --> "Gemini Adapter"
"LLM Port" --> "Mistral Adapter"
"LLM Port" --> "HuggingFace Adapter"

"Embedding Store Port" --> "PgVectorEmbeddingStore"
"Docker Service Port" --> "PgVectorService\n(docker-java)"
"Slides Renderer Port" --> "RevealJS Adapter"
"Git Repository Port" --> "Git Adapter"

"Gradle Task Adapter\n(RagTask)" --> "AssistantManager"
"Gradle Task Adapter\n(RagTask)" --> "RagManager"
"Gradle Task Adapter\n(RagTask)" --> "SlidesInitializer"
@enduml

This architecture delivers:

  • independence from LLM providers

  • swappable RAG store (pgvector today, any backend tomorrow)

  • ability to replace Reveal.js with another rendering engine

  • high testability through dependency injection

  • complete decoupling between Gradle and business logic

Roadmap

  • Configuration Cache support β€” blocked on asciidoctor-gradle 5.x stable release.

  • Cucumber tests for deck generation and the RAG pipeline.

  • Support for additional Reveal.js themes.

  • Extended DSL configuration (theme, transition, source dir).

  • Real-time incremental indexing when editing sources.

Note
The plugin explicitly declares configurationCache = false on the Gradle Plugin Portal.
Do not enable the Gradle Configuration Cache with this plugin β€” the asciidoctorRevealJs task
runs OUT_OF_PROCESS via JRuby and is not compatible in its current state.

License

This project is licensed under Apache‑2.0 β€” see the LICENSE file.

About

πŸ–ΌοΈ Gradle Kotlin DSL plugin for generating Reveal.js slide decks from Asciidoc source. Create, customize, and deploy your presentation as a static site directly from your build script.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors