This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new a342d94f4d68 camel-tui - Include YAML DSL model in camel-catalog for
runtime loading and prettify discard popup
a342d94f4d68 is described below
commit a342d94f4d68a0b01db91ae84074a55b00cc0c6b
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 6 11:36:54 2026 +0200
camel-tui - Include YAML DSL model in camel-catalog for runtime loading and
prettify discard popup
- Copy camelYamlDsl-model.json into camel-catalog schemas directory via
PrepareCatalogMojo so it is available at runtime through the catalog's
version manager classloader (adapts to the Camel version in use)
- Load the completion tree from the catalog instead of local classpath
- Prettify the discard confirmation popup: wider, centered message,
padded key hints in footer
- Document the model JSON in camel-catalog and yaml-dsl docs
camel-tui - Unify blank line indent derivation into shared methods
Replace 7 duplicate blank-line indent derivation blocks with two shared
methods:
- deriveBlankLineIndent: for context detection (min of predecessor and
successor indent)
- deriveInsertionIndent: for text insertion (predecessor only, with
- key: +4 and key: +2 offsets)
Both only activate on truly empty lines (0 spaces). Lines with actual
spaces use their indent directly. Fixes incorrect indentation when
inserting keys after list items like - setHeader:.
camel-tui - Add fallback schema path for completion tree and reset cache on
integration change
- Try both catalog schemas path and direct schema path when loading
the completion tree, for compatibility across Camel versions
- Reset completionTree cache when integration changes so it reloads
from the correct catalog version
camel-tui - Bundle YAML DSL model JSON in TUI plugin JAR via
maven-resources-plugin
Copy camelYamlDsl-model.json from camel-yaml-dsl into the TUI plugin
JAR during build, so the completion tree is always available without
depending on the catalog classloader resolving it at runtime. Falls
back to the catalog version manager for different Camel versions.
camel-tui - Add listChildren/isListItem hints to model JSON and simplify
indent derivation
- Model generator now marks nodes with listChildren (root, steps) and
isListItem (EIPs, top-level elements) flags for editor tooling
- Simplify indent derivation to use predecessor-only approach: after
- key: → indent + 4, after key: → indent + 2, after key: value → same
- Remove unreliable successor-based indent guessing that caused wrong
context detection when successor was in a different block
camel-tui - Prefix list item insertions with - and restyle popups to match
quit dialog
- EIP step completions from steps: now insert with - prefix (e.g. - bean:)
using the listChildren flag from the model JSON
- Discard and validation popups use inline bold key hints instead of
footer, matching the quit dialog style
- Discard flow changed to Enter confirm / Esc cancel (more intuitive)
camel-tui - Use bold + accent color for scope line highlight in edit mode
Makes the EIP/uri scope line stand out more clearly when editing YAML
routes, using the theme accent color instead of plain bold.
camel-tui - Smart Enter indentation, scope line fix, and steps sorted last
- Enter after key: auto-indents children (+2 or +4 for list items),
and prefixes with - for listChildren nodes (e.g., steps:)
- Fix scope line highlighting uri: instead of route: for blank lines
inside from: block
- Sort steps/outputs to bottom of completion popup since they are
always added last
camel-tui - Fix Tab completion on bare list item lines (- ) and scope line
- Strip bare - prefix same as - prefix so filter is empty, not -
- Return null from findEnclosingComponent for - prefixed lines (step
items, not parameters)
- No scope line highlight on bare - lines (no EIP selected yet)
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
Regen
---
.../camel/catalog/schemas}/camelYamlDsl-model.json | 85 +++++++
.../modules/ROOT/pages/camel-catalog.adoc | 9 +-
dsl/camel-jbang/camel-jbang-plugin-tui/pom.xml | 23 ++
.../jbang/core/commands/tui/AutocompletePopup.java | 10 +
.../dsl/jbang/core/commands/tui/SourceTab.java | 31 ++-
.../dsl/jbang/core/commands/tui/SourceViewer.java | 277 ++++++++++-----------
.../core/commands/tui/SourceViewerEditTest.java | 4 +-
.../core/commands/tui/YamlCompletionTest.java | 21 ++
.../maven/dsl/yaml/GenerateYamlCompletionMojo.java | 46 +++-
.../resources/schema/camelYamlDsl-model.json | 85 +++++++
.../camel/maven/packaging/PrepareCatalogMojo.java | 11 +
11 files changed, 452 insertions(+), 150 deletions(-)
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
similarity index 99%
copy from
dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
copy to
catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
index 9d022f1ff00c..ff289173abc1 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
@@ -3,6 +3,7 @@
"root" : {
"title" : "Root",
"description" : "Top-level YAML DSL elements",
+ "listChildren" : true,
"children" : [ {
"name" : "beans",
"type" : "object",
@@ -128,6 +129,7 @@
"steps" : {
"title" : "Steps",
"description" : "Processing steps that can be used inside a route",
+ "listChildren" : true,
"children" : [ {
"name" : "a2aSubTask",
"type" : "object",
@@ -781,12 +783,15 @@
} ]
},
"beansDeserializer" : {
+ "isListItem" : true,
"children" : [ ]
},
"dataFormatsDefinitionDeserializer" : {
+ "isListItem" : true,
"children" : [ ]
},
"errorHandlerDeserializer" : {
+ "isListItem" : true,
"children" : [ {
"name" : "deadLetterChannel",
"type" : "object",
@@ -846,6 +851,7 @@
} ]
},
"routeFromDefinitionDeserializer" : {
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string"
@@ -875,6 +881,7 @@
"a2ASubTask" : {
"title" : "A2A Sub Task",
"description" : "Groups route steps and emits A2A (Agent-to-Agent)
protocol progress events before, after, or when the grouped work fails",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -930,6 +937,7 @@
"aliases" : [ "fan-in", "reduce", "join", "merge" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1270,6 +1278,7 @@
"description" : "Calls a Java bean method",
"label" : "language,core,java",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1487,6 +1496,7 @@
"catch" : {
"title" : "Do Catch",
"description" : "Catches specific exceptions within a doTry block,
allowing fine-grained exception handling within the route",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -1531,6 +1541,7 @@
"label" : "eip,routing",
"aliases" : [ "router", "dispatch" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1609,6 +1620,7 @@
"aliases" : [ "circuit-breaker" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1700,6 +1712,7 @@
"label" : "eip,enrichment,routing",
"aliases" : [ "claim-check", "stash" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1827,6 +1840,7 @@
"convertBody" : {
"title" : "Convert Body To",
"description" : "Converts the message body to a specified Java type
using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1870,6 +1884,7 @@
"convertHeader" : {
"title" : "Convert Header To",
"description" : "Converts a message header value to a specified Java
type using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1924,6 +1939,7 @@
"convertVariable" : {
"title" : "Convert Variable To",
"description" : "Converts a variable value to a specified Java type
using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1987,6 +2003,7 @@
"label" : "eip,flowcontrol,routing",
"aliases" : [ "debounce", "delay" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2076,6 +2093,7 @@
"label" : "eip,routing",
"aliases" : [ "dispatch" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2164,6 +2182,7 @@
"label" : "eip,enrichment,transformation",
"aliases" : [ "enrich", "hydrate", "augment" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2611,6 +2630,7 @@
"aliases" : [ "filter" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2679,6 +2699,7 @@
"finally" : {
"title" : "Do Finally",
"description" : "Defines steps to always execute after a doTry block
completes, regardless of whether an exception was thrown or caught",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -2806,6 +2827,7 @@
"aliases" : [ "dedup", "deduplicate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2974,6 +2996,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3034,6 +3057,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3103,6 +3127,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3193,6 +3218,7 @@
"label" : "eip,endpoint,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "note",
"type" : "string",
@@ -3231,6 +3257,7 @@
"aliases" : [ "load-balance" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3308,6 +3335,7 @@
"description" : "Logs a message using an expression that can include
message body, headers, and other exchange data",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3415,6 +3443,7 @@
"aliases" : [ "loop", "iterate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3519,6 +3548,7 @@
"label" : "eip,dataformat,transformation",
"aliases" : [ "serialize" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3776,6 +3806,7 @@
"aliases" : [ "fan-out", "broadcast" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3951,6 +3982,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4075,6 +4107,7 @@
"label" : "error,errorhandling",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4550,6 +4583,7 @@
"description" : "Pauses a consumer based on a condition, allowing it to
be resumed later from the last known offset. Useful for controlling ingestion
rate on polling consumers.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4617,6 +4651,7 @@
"aliases" : [ "pipeline", "chain" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4666,6 +4701,7 @@
"description" : "Applies a policy to the route, such as a transactional
policy or a custom policy for cross-cutting concerns",
"label" : "configuration",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4726,6 +4762,7 @@
"label" : "eip,enrichment,routing",
"aliases" : [ "poll", "pull" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4805,6 +4842,7 @@
"label" : "eip,enrichment,transformation",
"aliases" : [ "enrich", "hydrate" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4963,6 +5001,7 @@
"description" : "Invokes a custom Camel Processor for programmatic
message processing in Java",
"label" : "eip,endpoint",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5071,6 +5110,7 @@
"description" : "Routes a message to a list of dynamically calculated
endpoints, determined at runtime from an expression. Each recipient receives a
copy of the message.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5555,6 +5595,7 @@
"description" : "Removes a specific header from the message by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5610,6 +5651,7 @@
"description" : "Removes all message headers whose names match a given
pattern. Useful for stripping internal Camel headers before sending to external
systems.",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5675,6 +5717,7 @@
"description" : "Removes all exchange properties whose names match a
given pattern",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5740,6 +5783,7 @@
"description" : "Removes a specific exchange property by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5795,6 +5839,7 @@
"description" : "Removes a specific variable by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5851,6 +5896,7 @@
"label" : "eip,flowcontrol,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6234,6 +6280,7 @@
"description" : "Enables resuming processing from the last known offset
after a restart, using a resume strategy to track and restore position",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6313,6 +6360,7 @@
"label" : "eip,errorhandling,routing",
"aliases" : [ "rollback" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6428,6 +6476,7 @@
"title" : "Route Configuration",
"description" : "Defines reusable configuration that is automatically
applied to matching routes, such as shared error handling or interceptors",
"label" : "configuration",
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6524,6 +6573,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6698,6 +6748,7 @@
"description" : "Defines a parameterized route template that can be
instantiated multiple times with different parameter values",
"label" : "configuration",
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6775,6 +6826,7 @@
"description" : "Routes a message through a pre-determined sequence of
endpoints defined in a header or expression (the routing slip). The list of
endpoints is evaluated once upfront.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6864,6 +6916,7 @@
"aliases" : [ "compensate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6983,6 +7036,7 @@
"sampling" : {
"title" : "Sample",
"description" : "Samples a subset of messages passing through the route,
either by frequency count or time interval, and discards the rest",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -7022,6 +7076,7 @@
"description" : "Executes an expression or script in a chosen language
for side effects without modifying the message body",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7079,6 +7134,7 @@
"label" : "eip,messaging,transformation",
"aliases" : [ "map", "transform" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7135,6 +7191,7 @@
"description" : "Sets the message exchange pattern (MEP) on the
exchange, such as InOnly for one-way or InOut for request-reply",
"label" : "configuration,messaging",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7191,6 +7248,7 @@
"description" : "Sets a message header to a value computed by an
expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7257,6 +7315,7 @@
"description" : "Sets multiple message headers at once, each computed by
its own expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7313,6 +7372,7 @@
"description" : "Sets an exchange property to a value computed by an
expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7379,6 +7439,7 @@
"description" : "Sets a variable to a value computed by an expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7445,6 +7506,7 @@
"description" : "Sets multiple variables at once, each computed by its
own expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7501,6 +7563,7 @@
"description" : "Sorts the items in a message body that is a List, using
a specified comparator or the default ordering",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7569,6 +7632,7 @@
"aliases" : [ "split", "chunk" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7825,6 +7889,7 @@
"label" : "eip,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7874,6 +7939,7 @@
"description" : "Stops processing of the current message and marks the
exchange as completed without sending it to any further steps",
"label" : "eip,flowcontrol,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7918,6 +7984,7 @@
"title" : "Templated Route",
"description" : "Creates a route instance from a route template, binding
specific parameter values to the template's parameters",
"label" : "configuration",
+ "isListItem" : true,
"children" : [ {
"name" : "routeTemplateRef",
"type" : "string",
@@ -8103,6 +8170,7 @@
"description" : "Offloads processing of subsequent steps in the route to
a thread pool, enabling asynchronous and concurrent message handling",
"label" : "eip,flowcontrol,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8249,6 +8317,7 @@
"label" : "eip,flowcontrol,routing",
"aliases" : [ "rate-limit", "throttle" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8379,6 +8448,7 @@
"description" : "Throws an exception during route processing, either
creating a new exception instance or re-throwing an existing one",
"label" : "error,errorhandling",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8453,6 +8523,7 @@
"label" : "eip,endpoint,routing",
"aliases" : [ "sink", "egress" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8538,6 +8609,7 @@
"toDynamic" : {
"title" : "To D",
"description" : "Sends the message to an endpoint URI computed
dynamically from an expression, allowing the destination to vary per message",
+ "isListItem" : true,
"children" : [ {
"name" : "allowOptimisedComponents",
"type" : "boolean",
@@ -8619,6 +8691,7 @@
"description" : "Tokenizes the message body for AI processing, splitting
text into chunks suitable for embedding or LLM input",
"label" : "eip,transformation,ai",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8690,6 +8763,7 @@
"description" : "Wraps the route in a transaction, ensuring that all
steps within the transacted block either complete together or roll back on
failure",
"label" : "configuration",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8748,6 +8822,7 @@
"description" : "Transforms the message body between known data types
(such as XML, JSON, Java objects) using registered data type transformers",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8813,6 +8888,7 @@
"label" : "eip,transformation",
"aliases" : [ "map", "transform" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8867,6 +8943,7 @@
"try" : {
"title" : "Do Try",
"description" : "Wraps route steps in a try-catch-finally block for
fine-grained exception handling within the route",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -8912,6 +8989,7 @@
"label" : "eip,dataformat,transformation",
"aliases" : [ "deserialize" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9227,6 +9305,7 @@
"label" : "eip,transformation",
"aliases" : [ "validate", "guard" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9371,6 +9450,7 @@
"label" : "eip,endpoint,routing",
"aliases" : [ "tap", "observe" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9581,6 +9661,7 @@
} ]
},
"sSLContextParameters" : {
+ "isListItem" : true,
"children" : [ {
"name" : "certAlias",
"type" : "string"
@@ -18189,6 +18270,7 @@
"title" : "Rest Configuration",
"description" : "Configures global settings for the REST DSL, such as
host, port, context path, binding mode, and the underlying HTTP component to
use",
"label" : "rest",
+ "isListItem" : true,
"children" : [ {
"name" : "component",
"type" : "enum",
@@ -18527,6 +18609,7 @@
"description" : "Defines a REST service with HTTP operations (GET, POST,
PUT, DELETE, etc.) using the Camel REST DSL",
"label" : "rest",
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -19575,6 +19658,7 @@
"title" : "Transformations",
"description" : "Container for defining data type transformer
definitions",
"label" : "transformation",
+ "isListItem" : true,
"children" : [ {
"name" : "customTransformer",
"type" : "object",
@@ -19689,6 +19773,7 @@
"title" : "Validations",
"description" : "Container for defining data type validator definitions",
"label" : "validation",
+ "isListItem" : true,
"children" : [ {
"name" : "customValidator",
"type" : "object",
diff --git a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
index 638294fee2ff..e50c178e9566 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-catalog.adoc
@@ -59,7 +59,7 @@ org
├── models-app (JSON schema)
├── others (JSON schema)
├── releases (JSON schema)
- ├── schemas (XML schema)
+ ├── schemas (XML and JSON schemas)
├── test-infra (JSON schema)
└── transformers (JSON schema)
----
@@ -72,6 +72,13 @@ The `docs` directory contains the AsciiDoc documentation
pages for all component
data formats, languages, and other artifacts. A `docs.properties` index file
lists all
available documentation names.
+The `schemas` directory contains the XML schemas for the XML DSLs
(`camel-spring.xsd`,
+`camel-xml-io.xsd`) and the YAML DSL model (`camelYamlDsl-model.json`).
+The YAML DSL model is a generated data structure describing every valid key at
every
+nesting level in the YAML DSL, with types, descriptions, required flags,
default values,
+and enum choices. It can be used by third-party tooling (editors, IDE plugins,
visual
+designers) to provide context-aware code completion and validation for YAML
DSL routes.
+
=== Documentation in the Catalog
The catalog includes the AsciiDoc documentation pages from all modules. This
makes the full
diff --git a/dsl/camel-jbang/camel-jbang-plugin-tui/pom.xml
b/dsl/camel-jbang/camel-jbang-plugin-tui/pom.xml
index 9f68638c6b2f..ee171389e468 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-tui/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-plugin-tui/pom.xml
@@ -135,6 +135,29 @@
<build>
<plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-yaml-dsl-model</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+
<outputDirectory>${project.build.outputDirectory}/schema</outputDirectory>
+ <resources>
+ <resource>
+
<directory>${project.basedir}/../../camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema</directory>
+ <includes>
+
<include>camelYamlDsl-model.json</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
index 4b57abc28d19..dcb2ca9a30b1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AutocompletePopup.java
@@ -440,6 +440,16 @@ class AutocompletePopup {
this.titlePrefix = titlePrefix;
}
+ private boolean listItemInsertion;
+
+ void setListItemInsertion(boolean listItemInsertion) {
+ this.listItemInsertion = listItemInsertion;
+ }
+
+ boolean isListItemInsertion() {
+ return listItemInsertion;
+ }
+
private void rebuildList() {
if (!filter.hasFilter()) {
filteredItems = new ArrayList<>(allItems);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
index e49dcd0daba4..a67e4ce45234 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
@@ -155,6 +155,8 @@ class SourceTab extends AbstractTab {
sourceViewer.reset();
focusOnViewer = false;
leftPanelWidth = -1;
+ completionTreeLoaded = false;
+ completionTree = null;
refreshFiles();
}
@@ -559,6 +561,7 @@ class SourceTab extends AbstractTab {
sourceViewer.setAutocompleteProvider(this::provideYamlKeyCompletions);
sourceViewer.setAutocompleteValueProvider(this::provideYamlValueCompletions);
sourceViewer.setEndpointValidator(this::validateYamlEndpoints);
+
sourceViewer.setListItemNodeChecker(this::isListChildrenNode);
} else {
sourceViewer.setAutocompleteProvider(null);
sourceViewer.setAutocompleteValueProvider(null);
@@ -1070,18 +1073,39 @@ class SourceTab extends AbstractTab {
return items;
}
+ private boolean isListChildrenNode(String nodeName) {
+ JsonObject node = getTreeNode(nodeName);
+ return node != null && Boolean.TRUE.equals(node.get("listChildren"));
+ }
+
private static final Set<String> TREE_BOILERPLATE = Set.of("id", "note",
"description", "disabled");
private JsonObject getCompletionTree() {
if (!completionTreeLoaded) {
completionTreeLoaded = true;
+ // try bundled resource first
try (var is =
getClass().getResourceAsStream("/schema/camelYamlDsl-model.json")) {
if (is != null) {
String json = new String(is.readAllBytes(),
java.nio.charset.StandardCharsets.UTF_8);
completionTree = (JsonObject)
org.apache.camel.util.json.Jsoner.deserialize(json);
}
} catch (Exception e) {
- // ignore — tree not available
+ // ignore
+ }
+ // fallback: try catalog version manager (may have a different
Camel version)
+ if (completionTree == null) {
+ CamelCatalog cat = getCatalog();
+ if (cat != null) {
+ try (var is = cat.getVersionManager()
+
.getResourceAsStream("org/apache/camel/catalog/schemas/camelYamlDsl-model.json"))
{
+ if (is != null) {
+ String json = new String(is.readAllBytes(),
java.nio.charset.StandardCharsets.UTF_8);
+ completionTree = (JsonObject)
org.apache.camel.util.json.Jsoner.deserialize(json);
+ }
+ } catch (Exception e) {
+ // ignore — tree not available for this Camel version
+ }
+ }
}
}
return completionTree;
@@ -1147,6 +1171,11 @@ class SourceTab extends AbstractTab {
}
items.sort(Comparator.comparing(AutocompletePopup.CompletionItem::deprecated)
+ .thenComparing((a, b) -> {
+ boolean aIsSteps = "steps".equals(a.key()) ||
"outputs".equals(a.key());
+ boolean bIsSteps = "steps".equals(b.key()) ||
"outputs".equals(b.key());
+ return Boolean.compare(aIsSteps, bIsSteps);
+ })
.thenComparing((a, b) -> Boolean.compare(b.required(),
a.required())));
return items;
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
index ffc6a7dabd19..e42a1f129d04 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
@@ -144,6 +144,7 @@ class SourceViewer {
private BiConsumer<String, Boolean> notificationCallback;
private AutocompletePopup.AutocompleteProvider autocompleteProvider;
private AutocompletePopup.ValueProvider autocompleteValueProvider;
+ private java.util.function.Predicate<String> listItemNodeChecker;
private AutocompletePopup autocompletePopup;
private boolean validateOnSave = true;
private org.apache.camel.dsl.yaml.validator.YamlValidator yamlValidator;
@@ -185,6 +186,10 @@ class SourceViewer {
this.autocompleteValueProvider = provider;
}
+ void setListItemNodeChecker(java.util.function.Predicate<String> checker) {
+ this.listItemNodeChecker = checker;
+ }
+
void setValidateOnSave(boolean validateOnSave) {
this.validateOnSave = validateOnSave;
}
@@ -284,11 +289,14 @@ class SourceViewer {
autocompletePopup = null;
return true;
}
- if (dirty && !pendingDiscard) {
+ if (pendingDiscard) {
+ pendingDiscard = false;
+ return true;
+ }
+ if (dirty) {
pendingDiscard = true;
return true;
}
- pendingDiscard = false;
exitEditMode();
return true;
}
@@ -464,12 +472,13 @@ class SourceViewer {
}
if (autocompletePopup != null) {
boolean wasValueMode = autocompletePopup.isValueMode();
+ boolean wasListItem = autocompletePopup.isListItemInsertion();
AutocompletePopup.Result result =
autocompletePopup.handleKeyEvent(ke);
if (result == AutocompletePopup.Result.CLOSED) {
AutocompletePopup.CompletionItem item =
autocompletePopup.consumeSelectedItem();
autocompletePopup = null;
if (item != null) {
- insertCompletion(item, wasValueMode);
+ insertCompletion(item, wasValueMode, wasListItem);
}
} else if (result == AutocompletePopup.Result.CURSOR_RIGHT) {
editState.moveCursorRight();
@@ -478,15 +487,20 @@ class SourceViewer {
}
return true;
}
- if (!ke.isCancel()) {
- pendingDiscard = false;
+ if (pendingDiscard) {
+ if (ke.isConfirm()) {
+ pendingDiscard = false;
+ exitEditMode();
+ } else if (ke.isCancel()) {
+ pendingDiscard = false;
+ }
+ return true;
}
if (ke.isCancel()) {
- if (dirty && !pendingDiscard) {
+ if (dirty) {
pendingDiscard = true;
return true;
}
- pendingDiscard = false;
exitEditMode();
return true;
}
@@ -502,8 +516,21 @@ class SourceViewer {
int prevRow = editState.cursorRow();
String prevLine = editState.getLine(prevRow);
int indent = countLeadingSpaces(prevLine);
+ String pt = prevLine.trim();
editState.insert('\n');
- if (indent > 0) {
+ if (isCamelYamlFile() && pt.endsWith(":")) {
+ // parent key with no value — indent deeper for children
+ String key = extractEipName(pt);
+ if (listItemNodeChecker != null && key != null
+ && listItemNodeChecker.test(dashToCamelCase(key))) {
+ // children are list items (e.g., steps:, root)
+ int childIndent = indent + (pt.startsWith("- ") ? 4 : 2);
+ editState.insert(" ".repeat(childIndent) + "- ");
+ } else {
+ int childIndent = indent + (pt.startsWith("- ") ? 4 : 2);
+ editState.insert(" ".repeat(childIndent));
+ }
+ } else if (indent > 0) {
editState.insert(" ".repeat(indent));
}
dirty = true;
@@ -628,15 +655,24 @@ class SourceViewer {
String cursorLine = editState.getLine(fromRow);
int cursorIndent = countLeadingSpaces(cursorLine);
+ // list items (- key:) are inside steps, not inside parameters
+ if (!cursorLine.isBlank() && cursorLine.trim().startsWith("- ")) {
+ return null;
+ }
+
// blank lines: find the nearest preceding non-blank line for context
if (cursorLine.isBlank()) {
for (int i = fromRow - 1; i >= 0; i--) {
String prev = editState.getLine(i);
if (!prev.isBlank()) {
- if (prev.trim().startsWith("parameters:")) {
+ String pt = prev.trim();
+ if (pt.startsWith("parameters:")) {
// blank line right after parameters: — cursor is
inside the block
cursorIndent = countLeadingSpaces(prev) + 1;
fromRow = i;
+ } else if (pt.startsWith("- ") || pt.startsWith("steps:"))
{
+ // inside a steps block or list item — not inside
parameters
+ return null;
} else {
return findEnclosingComponent(i);
}
@@ -662,8 +698,17 @@ class SourceViewer {
break;
}
if (i < fromRow && indent < cursorIndent &&
!trimmed.startsWith("#")) {
+ // stop if we hit a structural boundary (steps:, from:, etc.)
break;
}
+ // also stop if we hit a list item at a shallower or equal indent
— we've left the parameters scope
+ if (i < fromRow && indent <= cursorIndent) {
+ String key = extractEipName(trimmed);
+ if (key != null && ("steps".equals(key) || "from".equals(key)
+ || trimmed.startsWith("- "))) {
+ break;
+ }
+ }
}
if (parametersRow < 0) {
@@ -841,34 +886,36 @@ class SourceViewer {
record YamlEipContext(String eipName) {
}
+ int deriveBlankLineIndent(int fromRow) {
+ return deriveIndentFromPredecessor(fromRow);
+ }
+
+ private int deriveInsertionIndent(int fromRow) {
+ return deriveIndentFromPredecessor(fromRow);
+ }
+
+ private int deriveIndentFromPredecessor(int fromRow) {
+ for (int i = fromRow - 1; i >= 0; i--) {
+ String prev = editState.getLine(i);
+ if (prev.isBlank()) {
+ continue;
+ }
+ int indent = countLeadingSpaces(prev);
+ String pt = prev.trim();
+ if (pt.endsWith(":")) {
+ return indent + (pt.startsWith("- ") ? 4 : 2);
+ }
+ return indent;
+ }
+ return 0;
+ }
+
String findParentYamlKey(int fromRow) {
String cursorLine = editState.getLine(fromRow);
int cursorIndent = countLeadingSpaces(cursorLine);
if (cursorLine.isBlank() && cursorIndent == 0) {
- int lineCount = editState.lineCount();
- for (int i = fromRow + 1; i < lineCount; i++) {
- String next = editState.getLine(i);
- if (!next.isBlank()) {
- cursorIndent = countLeadingSpaces(next);
- break;
- }
- }
- if (cursorIndent == 0) {
- for (int i = fromRow - 1; i >= 0; i--) {
- String prev = editState.getLine(i);
- if (!prev.isBlank()) {
- cursorIndent = countLeadingSpaces(prev);
- String pt = prev.trim();
- if (pt.startsWith("- ") && pt.endsWith(":")) {
- cursorIndent += 4;
- } else if (pt.endsWith(":")) {
- cursorIndent += 2;
- }
- break;
- }
- }
- }
+ cursorIndent = deriveBlankLineIndent(fromRow);
}
// walk up to find parent key at lower indent
@@ -898,32 +945,8 @@ class SourceViewer {
String cursorLine = editState.getLine(fromRow);
int cursorIndent = countLeadingSpaces(cursorLine);
- // blank lines: derive indent from context
- if (cursorLine.isBlank()) {
- if (cursorIndent == 0) {
- // truly empty line — look at successor first, then predecessor
- int succIndent = -1;
- int lineCount = editState.lineCount();
- for (int i = fromRow + 1; i < lineCount; i++) {
- String next = editState.getLine(i);
- if (!next.isBlank()) {
- succIndent = countLeadingSpaces(next);
- break;
- }
- }
- if (succIndent > 0) {
- cursorIndent = succIndent;
- } else {
- for (int i = fromRow - 1; i >= 0; i--) {
- String prev = editState.getLine(i);
- if (!prev.isBlank()) {
- // treat cursor as sibling of predecessor
- cursorIndent = countLeadingSpaces(prev);
- break;
- }
- }
- }
- }
+ if (cursorLine.isBlank() && cursorIndent == 0) {
+ cursorIndent = deriveBlankLineIndent(fromRow);
}
// if cursor is inside a parameters: block, defer to component
completion
@@ -972,29 +995,8 @@ class SourceViewer {
String cursorLine = editState.getLine(fromRow);
int cursorIndent = countLeadingSpaces(cursorLine);
- // for blank lines, check both directions and use the deeper indent
(sibling level)
- if (cursorLine.isBlank()) {
- int succIndent = -1;
- int predIndent = -1;
- int lineCount2 = editState.lineCount();
- for (int i = fromRow + 1; i < lineCount2; i++) {
- String next = editState.getLine(i);
- if (!next.isBlank()) {
- succIndent = countLeadingSpaces(next);
- break;
- }
- }
- for (int i = fromRow - 1; i >= 0; i--) {
- String prev = editState.getLine(i);
- if (!prev.isBlank()) {
- predIndent = countLeadingSpaces(prev);
- break;
- }
- }
- cursorIndent = Math.max(succIndent, predIndent);
- if (cursorIndent < 0) {
- cursorIndent = 0;
- }
+ if (cursorLine.isBlank() && cursorIndent == 0) {
+ cursorIndent = deriveBlankLineIndent(fromRow);
}
// scan upward for siblings at same indent
@@ -1065,6 +1067,11 @@ class SourceViewer {
trimmed = trimmed.substring(2).trim();
}
+ // bare list item (- ) with no key yet — no scope
+ if (cursorLine.trim().startsWith("- ") && trimmed.isEmpty()) {
+ return -1;
+ }
+
// if cursor is on a uri: line, scope is this row
if (trimmed.startsWith("uri:")) {
return cursorRow;
@@ -1085,27 +1092,8 @@ class SourceViewer {
int cursorIndent = countLeadingSpaces(cursorLine);
- // for blank lines, derive indent from context
if (cursorLine.isBlank()) {
- if (cursorIndent == 0) {
- int lineCount = editState.lineCount();
- for (int i = cursorRow + 1; i < lineCount; i++) {
- String next = editState.getLine(i);
- if (!next.isBlank()) {
- cursorIndent = countLeadingSpaces(next);
- break;
- }
- }
- if (cursorIndent == 0) {
- for (int i = cursorRow - 1; i >= 0; i--) {
- String prev = editState.getLine(i);
- if (!prev.isBlank()) {
- cursorIndent = countLeadingSpaces(prev);
- break;
- }
- }
- }
- }
+ cursorIndent = deriveBlankLineIndent(cursorRow);
}
// walk up looking for the scope line
@@ -1129,6 +1117,16 @@ class SourceViewer {
if (eipName != null && !STRUCTURAL_KEYS.contains(eipName)) {
return i;
}
+ // for from:/to: blocks, look for uri: sibling as scope
+ if (eipName != null && ("from".equals(eipName) ||
CONSUMER_EIPS.contains(eipName)
+ || PRODUCER_EIPS.contains(eipName))) {
+ for (int j = i + 1; j < cursorRow; j++) {
+ String jl = editState.getLine(j);
+ if (!jl.isBlank() && jl.trim().startsWith("uri:")) {
+ return j;
+ }
+ }
+ }
cursorIndent = indent;
}
}
@@ -1286,6 +1284,8 @@ class SourceViewer {
String trimmed = lineText.trim();
if (trimmed.startsWith("- ")) {
trimmed = trimmed.substring(2).trim();
+ } else if (trimmed.equals("-")) {
+ trimmed = "";
}
// try component name completion on uri: lines first
@@ -1321,7 +1321,7 @@ class SourceViewer {
}
}
} else {
- String filter = colonIdx > 0 ? trimmed.substring(0,
colonIdx).trim() : trimmed;
+ String filter = trimmed;
String role = ctx.consumer() ? "consumer" : "producer";
java.util.Set<String> existing =
collectExistingParameters(row);
String context = "yaml:" + ctx.component() + ":" + role;
@@ -1341,6 +1341,7 @@ class SourceViewer {
if (autocompleteProvider != null) {
String parentKey = findParentYamlKey(row);
int colonIdx = trimmed.indexOf(':');
+
if (colonIdx > 0) {
// value completion
String optionName = trimmed.substring(0, colonIdx).trim();
@@ -1370,16 +1371,19 @@ class SourceViewer {
if (items != null && !items.isEmpty()) {
autocompletePopup = new AutocompletePopup(items, filter,
filter);
autocompletePopup.setTitlePrefix(parentKey);
+ if (listItemNodeChecker != null &&
listItemNodeChecker.test(parentKey)) {
+ autocompletePopup.setListItemInsertion(true);
+ }
}
}
}
}
- private void insertCompletion(AutocompletePopup.CompletionItem item,
boolean valueMode) {
+ private void insertCompletion(AutocompletePopup.CompletionItem item,
boolean valueMode, boolean listItem) {
dirty = true;
String currentLine = editState.getLine(editState.cursorRow());
if (isCamelYamlFile()) {
- insertYamlCompletion(item, valueMode, currentLine);
+ insertYamlCompletion(item, valueMode, currentLine, listItem);
} else {
insertPropertiesCompletion(item, valueMode, currentLine);
}
@@ -1411,34 +1415,14 @@ class SourceViewer {
}
void insertYamlCompletion(AutocompletePopup.CompletionItem item, boolean
valueMode, String currentLine) {
+ insertYamlCompletion(item, valueMode, currentLine, false);
+ }
+
+ private void insertYamlCompletion(
+ AutocompletePopup.CompletionItem item, boolean valueMode, String
currentLine, boolean listItem) {
int indent = countLeadingSpaces(currentLine);
- // blank lines: derive indent from context
- if (currentLine.isBlank() && indent == 0) {
- int row = editState.cursorRow();
- // look at next non-blank line first (sibling indent)
- int lineCount = editState.lineCount();
- for (int i = row + 1; i < lineCount; i++) {
- String next = editState.getLine(i);
- if (!next.isBlank()) {
- indent = countLeadingSpaces(next);
- break;
- }
- }
- if (indent == 0) {
- // no successor — derive from predecessor
- for (int i = row - 1; i >= 0; i--) {
- String prev = editState.getLine(i);
- if (!prev.isBlank()) {
- indent = countLeadingSpaces(prev);
- String trimmed = prev.trim();
- if (trimmed.endsWith(":")) {
- // cursor is inside this block — indent deeper
- indent += trimmed.startsWith("- ") ? 4 : 2;
- }
- break;
- }
- }
- }
+ if (currentLine.isBlank()) {
+ indent = deriveInsertionIndent(editState.cursorRow());
}
String indentStr = " ".repeat(indent);
@@ -1476,10 +1460,11 @@ class SourceViewer {
}
}
} else {
- editState.insert(indentStr + item.key() + ":");
+ String prefix = listItem ? "- " : "";
+ editState.insert(indentStr + prefix + item.key() + ":");
if ("object".equals(item.type()) || "array".equals(item.type())) {
editState.insert('\n');
- editState.insert(indentStr + " ");
+ editState.insert(indentStr + (listItem ? " " : " "));
} else {
editState.insert(' ');
}
@@ -1663,12 +1648,13 @@ class SourceViewer {
if (editMode) {
if (autocompletePopup != null) {
boolean wasValueMode = autocompletePopup.isValueMode();
+ boolean wasListItem = autocompletePopup.isListItemInsertion();
AutocompletePopup.Result result =
autocompletePopup.handleMouseEvent(me);
if (result == AutocompletePopup.Result.CLOSED) {
AutocompletePopup.CompletionItem item =
autocompletePopup.consumeSelectedItem();
autocompletePopup = null;
if (item != null) {
- insertCompletion(item, wasValueMode);
+ insertCompletion(item, wasValueMode, wasListItem);
}
}
return true;
@@ -1932,7 +1918,7 @@ class SourceViewer {
if (relativeRow >= 0 && relativeRow < inner.height()) {
int screenY = inner.top() + relativeRow;
Rect lineRect = new Rect(inner.left(), screenY,
inner.width(), 1);
- frame.buffer().setStyle(lineRect, Style.EMPTY.bold());
+ frame.buffer().setStyle(lineRect,
Style.EMPTY.bold().fg(Theme.accent()));
}
}
}
@@ -1963,6 +1949,9 @@ class SourceViewer {
String msg = validationErrors.get(i);
wrapText(msg, innerW, allLines);
}
+ allLines.add(Line.empty());
+ allLines.add(Line.from(Span.raw(" "),
+ Span.styled("Esc", Style.EMPTY.bold()), Span.raw(" close")));
int contentH = allLines.size();
int popupH = Math.min(contentH + 2, area.height() - 4);
@@ -1977,8 +1966,6 @@ class SourceViewer {
Block block = Block.builder()
.borderType(BorderType.ROUNDED).borders(Borders.ALL)
.title(Title.from(Line.from(Span.styled(titleText,
Theme.error().bold()))))
- .titleBottom(Title.from(Line.from(
- Span.styled(" Esc", Theme.hintKey()), Span.raw(" close
"))))
.build();
frame.renderWidget(block, popup);
Rect inner = block.inner(popup);
@@ -1997,9 +1984,8 @@ class SourceViewer {
}
private void renderDiscardPopup(Frame frame, Rect area) {
- String msg = "Unsaved changes will be lost.";
- int popupW = Math.min(msg.length() + 6, area.width() - 4);
- int popupH = 5;
+ int popupW = Math.min(40, area.width() - 4);
+ int popupH = 6;
int x = area.left() + Math.max(0, (area.width() - popupW) / 2);
int y = area.top() + Math.max(0, (area.height() - popupH) / 2);
Rect popup = new Rect(x, y, popupW, popupH);
@@ -2009,16 +1995,19 @@ class SourceViewer {
Block block = Block.builder()
.borderType(BorderType.ROUNDED).borders(Borders.ALL)
.title(Title.from(Line.from(Span.styled(" Discard Changes? ",
Theme.warning().bold()))))
- .titleBottom(Title.from(Line.from(
- Span.styled(" Esc", Theme.hintKey()), Span.raw("
discard "),
- Span.styled("any key", Theme.hintKey()), Span.raw("
cancel "))))
.build();
frame.renderWidget(block, popup);
Rect inner = block.inner(popup);
frame.renderWidget(
Paragraph.builder().text(Text.from(
- Line.from(Span.styled(msg, Theme.warning())))).build(),
+ Line.empty(),
+ Line.from(Span.raw(" Unsaved changes will be lost.")),
+ Line.empty(),
+ Line.from(Span.raw(" "),
+ Span.styled("Enter", Style.EMPTY.bold()),
Span.raw(" confirm "),
+ Span.styled("Esc", Style.EMPTY.bold()),
Span.raw(" cancel"))))
+ .build(),
inner);
}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
index a70e9610fab2..748aa9e38b79 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewerEditTest.java
@@ -150,10 +150,10 @@ class SourceViewerEditTest {
for (char ch : "CHANGED".toCharArray()) {
viewer.handleKeyEvent(KeyEvent.ofChar(ch, KeyModifiers.NONE));
}
- // first Esc shows discard warning, second Esc confirms
+ // first Esc shows discard warning, Enter confirms discard
viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ESCAPE,
KeyModifiers.NONE));
assertThat(viewer.isEditMode()).isTrue();
- viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ESCAPE,
KeyModifiers.NONE));
+ viewer.handleKeyEvent(KeyEvent.ofKey(KeyCode.ENTER,
KeyModifiers.NONE));
assertThat(viewer.isEditMode()).isFalse();
assertThat(Files.readString(sourceFile,
StandardCharsets.UTF_8)).isEqualTo(original);
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
index 6c09dd13d2d7..fa636d202aab 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/YamlCompletionTest.java
@@ -1049,6 +1049,27 @@ class YamlCompletionTest {
assertThat(viewer.findParentYamlKey(3)).isEqualTo("steps");
}
+ @Test
+ void findParentYamlKeyOnDashLineInsideSteps() throws IOException {
+ String yaml = String.join("\n",
+ "- from:",
+ " uri: timer:tick",
+ " steps:",
+ " - ",
+ "");
+
+ Path file = tempDir.resolve("route.camel.yaml");
+ Files.writeString(file, yaml);
+
+ SourceViewer viewer = new SourceViewer();
+ viewer.loadFile(file);
+ viewer.enterEditMode();
+
+ // " - " is a list item starter inside steps
+ assertThat(viewer.findParentYamlKey(3)).isEqualTo("steps");
+ assertThat(viewer.findEnclosingComponent(3)).isNull();
+ }
+
@Test
void findParentYamlKeyInsideRoute() throws IOException {
String yaml = String.join("\n",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlCompletionMojo.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlCompletionMojo.java
index e4b04711e77f..bd9448ce10db 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlCompletionMojo.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlCompletionMojo.java
@@ -22,10 +22,12 @@ import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -99,6 +101,27 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
ObjectNode nodes = mapper.createObjectNode();
root.set("nodes", nodes);
+ // collect which FQ definitions are step-capable (children of
ProcessorDefinition)
+ Set<String> stepDefinitions = new java.util.HashSet<>();
+ JsonNode processorDef = definitions.get(PROCESSOR_DEFINITION);
+ if (processorDef != null && processorDef.has("properties")) {
+ processorDef.get("properties").fields().forEachRemaining(e -> {
+ String ref = resolveRefFq(e.getValue());
+ if (ref != null) {
+ stepDefinitions.add(ref);
+ }
+ });
+ }
+
+ // collect which FQ definitions are top-level elements
+ Set<String> topLevelDefinitions = new java.util.HashSet<>();
+ topLevelProps.fields().forEachRemaining(e -> {
+ String ref = resolveRefFq(e.getValue());
+ if (ref != null) {
+ topLevelDefinitions.add(ref);
+ }
+ });
+
// root node — top-level elements
buildRootNode(nodes, topLevelProps);
@@ -109,7 +132,7 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
buildExpressionNode(nodes, definitions);
// all other definition nodes (EIPs, languages, data formats, etc.)
- buildDefinitionNodes(nodes, definitions);
+ buildDefinitionNodes(nodes, definitions, stepDefinitions,
topLevelDefinitions);
// write output
StringWriter sw = new StringWriter();
@@ -170,6 +193,7 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("title", "Root");
rootNode.put("description", "Top-level YAML DSL elements");
+ rootNode.put("listChildren", true);
ArrayNode children = mapper.createArrayNode();
Iterator<Map.Entry<String, JsonNode>> fields = topLevelProps.fields();
@@ -207,6 +231,7 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
ObjectNode stepsNode = mapper.createObjectNode();
stepsNode.put("title", "Steps");
stepsNode.put("description", "Processing steps that can be used inside
a route");
+ stepsNode.put("listChildren", true);
ArrayNode children = mapper.createArrayNode();
JsonNode props = processorDef.get("properties");
@@ -293,7 +318,9 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
nodes.set("expression", exprNode);
}
- private void buildDefinitionNodes(ObjectNode nodes, JsonNode definitions) {
+ private void buildDefinitionNodes(
+ ObjectNode nodes, JsonNode definitions,
+ Set<String> stepDefinitions, Set<String> topLevelDefinitions) {
Iterator<Map.Entry<String, JsonNode>> fields = definitions.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
@@ -318,6 +345,11 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
ObjectNode node = mapper.createObjectNode();
enrichNodeMetadata(node, nodeName, fqName, def);
+ // mark nodes that appear as list items (in steps or at root level)
+ if (stepDefinitions.contains(fqName) ||
topLevelDefinitions.contains(fqName)) {
+ node.put("isListItem", true);
+ }
+
ArrayNode children = buildChildrenFromDefinition(def, fqName);
node.set("children", children);
@@ -525,6 +557,16 @@ public class GenerateYamlCompletionMojo extends
AbstractMojo {
return optMap;
}
+ private String resolveRefFq(JsonNode prop) {
+ if (prop.has("$ref")) {
+ return prop.get("$ref").asText().replace("#/items/definitions/",
"");
+ }
+ if (prop.has("items") && prop.get("items").has("$ref")) {
+ return
prop.get("items").get("$ref").asText().replace("#/items/definitions/", "");
+ }
+ return null;
+ }
+
private String resolveRef(JsonNode prop) {
// direct $ref
if (prop.has("$ref")) {
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
index 9d022f1ff00c..ff289173abc1 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
@@ -3,6 +3,7 @@
"root" : {
"title" : "Root",
"description" : "Top-level YAML DSL elements",
+ "listChildren" : true,
"children" : [ {
"name" : "beans",
"type" : "object",
@@ -128,6 +129,7 @@
"steps" : {
"title" : "Steps",
"description" : "Processing steps that can be used inside a route",
+ "listChildren" : true,
"children" : [ {
"name" : "a2aSubTask",
"type" : "object",
@@ -781,12 +783,15 @@
} ]
},
"beansDeserializer" : {
+ "isListItem" : true,
"children" : [ ]
},
"dataFormatsDefinitionDeserializer" : {
+ "isListItem" : true,
"children" : [ ]
},
"errorHandlerDeserializer" : {
+ "isListItem" : true,
"children" : [ {
"name" : "deadLetterChannel",
"type" : "object",
@@ -846,6 +851,7 @@
} ]
},
"routeFromDefinitionDeserializer" : {
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string"
@@ -875,6 +881,7 @@
"a2ASubTask" : {
"title" : "A2A Sub Task",
"description" : "Groups route steps and emits A2A (Agent-to-Agent)
protocol progress events before, after, or when the grouped work fails",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -930,6 +937,7 @@
"aliases" : [ "fan-in", "reduce", "join", "merge" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1270,6 +1278,7 @@
"description" : "Calls a Java bean method",
"label" : "language,core,java",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1487,6 +1496,7 @@
"catch" : {
"title" : "Do Catch",
"description" : "Catches specific exceptions within a doTry block,
allowing fine-grained exception handling within the route",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -1531,6 +1541,7 @@
"label" : "eip,routing",
"aliases" : [ "router", "dispatch" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1609,6 +1620,7 @@
"aliases" : [ "circuit-breaker" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1700,6 +1712,7 @@
"label" : "eip,enrichment,routing",
"aliases" : [ "claim-check", "stash" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -1827,6 +1840,7 @@
"convertBody" : {
"title" : "Convert Body To",
"description" : "Converts the message body to a specified Java type
using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1870,6 +1884,7 @@
"convertHeader" : {
"title" : "Convert Header To",
"description" : "Converts a message header value to a specified Java
type using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1924,6 +1939,7 @@
"convertVariable" : {
"title" : "Convert Variable To",
"description" : "Converts a variable value to a specified Java type
using Camel's built-in type converters",
+ "isListItem" : true,
"children" : [ {
"name" : "charset",
"type" : "string",
@@ -1987,6 +2003,7 @@
"label" : "eip,flowcontrol,routing",
"aliases" : [ "debounce", "delay" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2076,6 +2093,7 @@
"label" : "eip,routing",
"aliases" : [ "dispatch" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2164,6 +2182,7 @@
"label" : "eip,enrichment,transformation",
"aliases" : [ "enrich", "hydrate", "augment" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2611,6 +2630,7 @@
"aliases" : [ "filter" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2679,6 +2699,7 @@
"finally" : {
"title" : "Do Finally",
"description" : "Defines steps to always execute after a doTry block
completes, regardless of whether an exception was thrown or caught",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -2806,6 +2827,7 @@
"aliases" : [ "dedup", "deduplicate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -2974,6 +2996,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3034,6 +3057,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3103,6 +3127,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3193,6 +3218,7 @@
"label" : "eip,endpoint,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "note",
"type" : "string",
@@ -3231,6 +3257,7 @@
"aliases" : [ "load-balance" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3308,6 +3335,7 @@
"description" : "Logs a message using an expression that can include
message body, headers, and other exchange data",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3415,6 +3443,7 @@
"aliases" : [ "loop", "iterate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3519,6 +3548,7 @@
"label" : "eip,dataformat,transformation",
"aliases" : [ "serialize" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3776,6 +3806,7 @@
"aliases" : [ "fan-out", "broadcast" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -3951,6 +3982,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4075,6 +4107,7 @@
"label" : "error,errorhandling",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4550,6 +4583,7 @@
"description" : "Pauses a consumer based on a condition, allowing it to
be resumed later from the last known offset. Useful for controlling ingestion
rate on polling consumers.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4617,6 +4651,7 @@
"aliases" : [ "pipeline", "chain" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4666,6 +4701,7 @@
"description" : "Applies a policy to the route, such as a transactional
policy or a custom policy for cross-cutting concerns",
"label" : "configuration",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4726,6 +4762,7 @@
"label" : "eip,enrichment,routing",
"aliases" : [ "poll", "pull" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4805,6 +4842,7 @@
"label" : "eip,enrichment,transformation",
"aliases" : [ "enrich", "hydrate" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -4963,6 +5001,7 @@
"description" : "Invokes a custom Camel Processor for programmatic
message processing in Java",
"label" : "eip,endpoint",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5071,6 +5110,7 @@
"description" : "Routes a message to a list of dynamically calculated
endpoints, determined at runtime from an expression. Each recipient receives a
copy of the message.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5555,6 +5595,7 @@
"description" : "Removes a specific header from the message by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5610,6 +5651,7 @@
"description" : "Removes all message headers whose names match a given
pattern. Useful for stripping internal Camel headers before sending to external
systems.",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5675,6 +5717,7 @@
"description" : "Removes all exchange properties whose names match a
given pattern",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5740,6 +5783,7 @@
"description" : "Removes a specific exchange property by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5795,6 +5839,7 @@
"description" : "Removes a specific variable by name",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -5851,6 +5896,7 @@
"label" : "eip,flowcontrol,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6234,6 +6280,7 @@
"description" : "Enables resuming processing from the last known offset
after a restart, using a resume strategy to track and restore position",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6313,6 +6360,7 @@
"label" : "eip,errorhandling,routing",
"aliases" : [ "rollback" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6428,6 +6476,7 @@
"title" : "Route Configuration",
"description" : "Defines reusable configuration that is automatically
applied to matching routes, such as shared error handling or interceptors",
"label" : "configuration",
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6524,6 +6573,7 @@
"label" : "configuration",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6698,6 +6748,7 @@
"description" : "Defines a parameterized route template that can be
instantiated multiple times with different parameter values",
"label" : "configuration",
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6775,6 +6826,7 @@
"description" : "Routes a message through a pre-determined sequence of
endpoints defined in a header or expression (the routing slip). The list of
endpoints is evaluated once upfront.",
"label" : "eip,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6864,6 +6916,7 @@
"aliases" : [ "compensate" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -6983,6 +7036,7 @@
"sampling" : {
"title" : "Sample",
"description" : "Samples a subset of messages passing through the route,
either by frequency count or time interval, and discards the rest",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -7022,6 +7076,7 @@
"description" : "Executes an expression or script in a chosen language
for side effects without modifying the message body",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7079,6 +7134,7 @@
"label" : "eip,messaging,transformation",
"aliases" : [ "map", "transform" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7135,6 +7191,7 @@
"description" : "Sets the message exchange pattern (MEP) on the
exchange, such as InOnly for one-way or InOut for request-reply",
"label" : "configuration,messaging",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7191,6 +7248,7 @@
"description" : "Sets a message header to a value computed by an
expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7257,6 +7315,7 @@
"description" : "Sets multiple message headers at once, each computed by
its own expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7313,6 +7372,7 @@
"description" : "Sets an exchange property to a value computed by an
expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7379,6 +7439,7 @@
"description" : "Sets a variable to a value computed by an expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7445,6 +7506,7 @@
"description" : "Sets multiple variables at once, each computed by its
own expression",
"label" : "eip,messaging,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7501,6 +7563,7 @@
"description" : "Sorts the items in a message body that is a List, using
a specified comparator or the default ordering",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7569,6 +7632,7 @@
"aliases" : [ "split", "chunk" ],
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7825,6 +7889,7 @@
"label" : "eip,routing",
"input" : true,
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7874,6 +7939,7 @@
"description" : "Stops processing of the current message and marks the
exchange as completed without sending it to any further steps",
"label" : "eip,flowcontrol,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -7918,6 +7984,7 @@
"title" : "Templated Route",
"description" : "Creates a route instance from a route template, binding
specific parameter values to the template's parameters",
"label" : "configuration",
+ "isListItem" : true,
"children" : [ {
"name" : "routeTemplateRef",
"type" : "string",
@@ -8103,6 +8170,7 @@
"description" : "Offloads processing of subsequent steps in the route to
a thread pool, enabling asynchronous and concurrent message handling",
"label" : "eip,flowcontrol,routing",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8249,6 +8317,7 @@
"label" : "eip,flowcontrol,routing",
"aliases" : [ "rate-limit", "throttle" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8379,6 +8448,7 @@
"description" : "Throws an exception during route processing, either
creating a new exception instance or re-throwing an existing one",
"label" : "error,errorhandling",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8453,6 +8523,7 @@
"label" : "eip,endpoint,routing",
"aliases" : [ "sink", "egress" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8538,6 +8609,7 @@
"toDynamic" : {
"title" : "To D",
"description" : "Sends the message to an endpoint URI computed
dynamically from an expression, allowing the destination to vary per message",
+ "isListItem" : true,
"children" : [ {
"name" : "allowOptimisedComponents",
"type" : "boolean",
@@ -8619,6 +8691,7 @@
"description" : "Tokenizes the message body for AI processing, splitting
text into chunks suitable for embedding or LLM input",
"label" : "eip,transformation,ai",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8690,6 +8763,7 @@
"description" : "Wraps the route in a transaction, ensuring that all
steps within the transacted block either complete together or roll back on
failure",
"label" : "configuration",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8748,6 +8822,7 @@
"description" : "Transforms the message body between known data types
(such as XML, JSON, Java objects) using registered data type transformers",
"label" : "eip,transformation",
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8813,6 +8888,7 @@
"label" : "eip,transformation",
"aliases" : [ "map", "transform" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -8867,6 +8943,7 @@
"try" : {
"title" : "Do Try",
"description" : "Wraps route steps in a try-catch-finally block for
fine-grained exception handling within the route",
+ "isListItem" : true,
"children" : [ {
"name" : "description",
"type" : "string",
@@ -8912,6 +8989,7 @@
"label" : "eip,dataformat,transformation",
"aliases" : [ "deserialize" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9227,6 +9305,7 @@
"label" : "eip,transformation",
"aliases" : [ "validate", "guard" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9371,6 +9450,7 @@
"label" : "eip,endpoint,routing",
"aliases" : [ "tap", "observe" ],
"input" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -9581,6 +9661,7 @@
} ]
},
"sSLContextParameters" : {
+ "isListItem" : true,
"children" : [ {
"name" : "certAlias",
"type" : "string"
@@ -18189,6 +18270,7 @@
"title" : "Rest Configuration",
"description" : "Configures global settings for the REST DSL, such as
host, port, context path, binding mode, and the underlying HTTP component to
use",
"label" : "rest",
+ "isListItem" : true,
"children" : [ {
"name" : "component",
"type" : "enum",
@@ -18527,6 +18609,7 @@
"description" : "Defines a REST service with HTTP operations (GET, POST,
PUT, DELETE, etc.) using the Camel REST DSL",
"label" : "rest",
"output" : true,
+ "isListItem" : true,
"children" : [ {
"name" : "id",
"type" : "string",
@@ -19575,6 +19658,7 @@
"title" : "Transformations",
"description" : "Container for defining data type transformer
definitions",
"label" : "transformation",
+ "isListItem" : true,
"children" : [ {
"name" : "customTransformer",
"type" : "object",
@@ -19689,6 +19773,7 @@
"title" : "Validations",
"description" : "Container for defining data type validator definitions",
"label" : "validation",
+ "isListItem" : true,
"children" : [ {
"name" : "customValidator",
"type" : "object",
diff --git
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
index 09a0d6ae29cf..cfd0d6d071dc 100644
---
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
+++
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/PrepareCatalogMojo.java
@@ -225,6 +225,12 @@ public class PrepareCatalogMojo extends AbstractMojo {
@Parameter(defaultValue =
"${project.basedir}/../../core/camel-xml-io/target/classes")
protected File xmlioSchemaDir;
+ /**
+ * The directory where the camel-yaml-dsl model schema are
+ */
+ @Parameter(defaultValue =
"${project.basedir}/../../dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema")
+ protected File yamlDslSchemaDir;
+
/**
* The directory where the camel-main metadata are
*/
@@ -1026,6 +1032,11 @@ public class PrepareCatalogMojo extends AbstractMojo {
copyFile(this.springSchemaDir.toPath().resolve("camel-spring.xsd"),
schemasOutDir);
getLog().info("Copying XML-IO schema");
copyFile(this.xmlioSchemaDir.toPath().resolve("camel-xml-io.xsd"),
schemasOutDir);
+ Path yamlModel =
this.yamlDslSchemaDir.toPath().resolve("camelYamlDsl-model.json");
+ if (Files.exists(yamlModel)) {
+ getLog().info("Copying YAML DSL model");
+ copyFile(yamlModel, schemasOutDir);
+ }
}
protected void executeMain() throws Exception {