Add configurable match-info extension support

Change-Id: I642c7863f1249025c518ee3402536fb07b26e529
diff --git a/Changes b/Changes
index e0576b0..07bb314 100644
--- a/Changes
+++ b/Changes
@@ -1,4 +1,4 @@
-0.66 2026-08-03
+0.66 2026-09-02
         - Fix link to NEGRA corpus (diewald)
         - Use M::P::Localize from CPAN (diewald)
         - Update dependency to Mojolicious (diewald)
@@ -11,6 +11,8 @@
           (diewald)
         - Fix shift issue when clicking on empty sidebar(uyen-nhu, closes #242)
         - Fix 'Query by match' constraint line (uyen-nhu, closes #276)
+        - Add configurable match-info extension (fixes part of Krill-Issue #271;
+          diewald; with AI assistance, GPT-5.4 Mini)
 
 0.65 2026-04-21
         - Expose colors and forms to plugins (diewald)
diff --git a/README.md b/README.md
index 43039c4..0800d9f 100644
--- a/README.md
+++ b/README.md
@@ -332,7 +332,7 @@
 
 ### Original Software
 
-Copyright (C) 2015-2025, [IDS Mannheim](https://www.ids-mannheim.de/)<br>
+Copyright (C) 2015-2026, [IDS Mannheim](https://www.ids-mannheim.de/)<br>
 Author: [Nils Diewald](https://www.nils-diewald.de/), Helge Stallkamp<br>
 Contributor: Uyen-Nhu Tran, Eliza Margaretha (Documentation), Susanne Feix
 and Rebecca Wilm (Translation), Leo Repp
diff --git a/dev/js/src/api.js b/dev/js/src/api.js
index effa9f8..a718d8a 100644
--- a/dev/js/src/api.js
+++ b/dev/js/src/api.js
@@ -24,6 +24,9 @@
 
     // match is a KorAP.Match object
     let url = KorAP.URL + '/corpus';
+    const matchInfoExtended = document.body
+      ? document.body.getAttribute('data-match-info-extended')
+      : null;
     /*
       url += '/' + match.corpusID;
       url += '/' + match.docID;
@@ -56,7 +59,7 @@
         docFragment += '/'+param['layer'];
       }
     }
-    
+
     // { spans : false, layer: [Array of KorAP.InfoLayer] }
     else {
       // TODO
@@ -64,6 +67,11 @@
       url += 'spans=false';
     };
 
+    if (matchInfoExtended) {
+      url += '&extended=' + encodeURIComponent(matchInfoExtended);
+      docFragment += ' +extended=' + matchInfoExtended;
+    }
+
     if (KorAP.ResponsePipe != null)
       url += '&response-pipe=' + KorAP.ResponsePipe.toString();
 
diff --git a/kalamar.conf b/kalamar.conf
index e0ff62c..d364d97 100644
--- a/kalamar.conf
+++ b/kalamar.conf
@@ -71,6 +71,7 @@
     #   items_per_page => 20,
     #   context => '20-t,20-t',
     #   alignment => 'left' # or 'right' or 'center'
+    #   match_info_extended => 'max',
     # },
 
     # proxies => [{
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 0d835e9..99e7d8f 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -347,7 +347,7 @@
 
   if (exists $conf->{defaults}) {
     my $def = $conf->{defaults};
-    foreach (qw!items_per_page context alignment!) {
+    foreach (qw!items_per_page context alignment match_info_extended!) {
       $self->defaults($_ => $def->{$_}) if $def->{$_};
     };
   };
diff --git a/lib/Kalamar/Controller/Search.pm b/lib/Kalamar/Controller/Search.pm
index 70fceac..472fd31 100644
--- a/lib/Kalamar/Controller/Search.pm
+++ b/lib/Kalamar/Controller/Search.pm
@@ -482,6 +482,7 @@
   $v->optional('foundry');
   $v->optional('layer');
   $v->optional('spans')->in(qw/true false/);
+  $v->optional('extended', 'trim');
   $v->optional('response-pipe', 'trim');
 
   # Check validation
@@ -518,6 +519,10 @@
     $query{spans} = $v->param('spans') if $v->param('spans');
   };
 
+  if (defined $v->param('extended') && $v->param('extended') ne '') {
+    $query{extended} = $v->param('extended');
+  };
+
   # Response pipe
   if ($v->param('response-pipe')) {
 
@@ -814,6 +819,11 @@
 Boolean value - either C<true> or C<false> - indicating, whether span information
 (i.e. for tree structures) should be retrieved.
 
+=item B<extended>
+
+Optional value forwarded to the backend match-info request. Configure this to
+request wider match context, for example C<max>.
+
 =back
 
 
diff --git a/t/match_info.t b/t/match_info.t
index edd84b4..1230e7d 100644
--- a/t/match_info.t
+++ b/t/match_info.t
@@ -10,7 +10,13 @@
 my $mount_point = '/realapi/';
 $ENV{KALAMAR_API} = $mount_point;
 
-my $t = Test::Mojo->new('Kalamar');
+my $t = Test::Mojo->new('Kalamar' => {
+  Kalamar => {
+    defaults => {
+      match_info_extended => 'max'
+    }
+  }
+});
 
 # Mount fake backend
 # Get the fixture path
@@ -24,6 +30,12 @@
 # Configure fake backend
 $fake_backend->pattern->defaults->{app}->log($t->app->log);
 
+# The config is exposed on rendered pages
+$t->get_ok('/')
+  ->status_is(200)
+  ->element_exists('body[data-match-info-extended="max"]')
+  ;
+
 # Query passed
 $t->get_ok('/corpus/WPD15/232/39681/p2133-2134?spans=false&foundry=*&_format=json')
   ->status_is(200)
@@ -40,6 +52,12 @@
   ->json_is('/meta/responsePipes','glemm')
   ;
 
+# Extended match-info request is accepted and forwarded
+$t->get_ok('/corpus/GOE/AGF/02286/p75682-75683?extended=max&_format=json')
+  ->status_is(200)
+  ->json_is('/textSigle', 'GOE/AGF/02286')
+  ;
+
 # TODO:
 #   It's surprising, that it doesn't return a 404!
 $t->get_ok('/corpus/notfound/X/X/p0-1?_format=json')
diff --git a/templates/layouts/main.html.ep b/templates/layouts/main.html.ep
index b3bd049..fe50269 100644
--- a/templates/layouts/main.html.ep
+++ b/templates/layouts/main.html.ep
@@ -60,6 +60,7 @@
         data-korap-url="<%== $api %>"
         data-hint-foundries="<%= join(',', @{stash('hint_foundries') // []}) %>"
         data-vc-helper-fields="<%= stash('vc_helper_fields') // '' %>"
+        data-match-info-extended="<%= stash('match_info_extended') // '' %>"
         itemscope
         itemtype="http://schema.org/<%= stash('schematype') || 'WebApplication' %>">