diff --git a/app/apps/api/serializers.py b/app/apps/api/serializers.py
index e69add839008d6dc165a8575f89a6b53b472510a..220b4642cad65332ddbcfe54e75cdc545788d109 100644
--- a/app/apps/api/serializers.py
+++ b/app/apps/api/serializers.py
@@ -19,6 +19,7 @@ from core.models import (Document,
                          LineTranscription,
                          BlockType,
                          LineType,
+                         Script,
                          OcrModel)
 from core.tasks import (segtrain, train, segment, transcribe)
 
@@ -51,6 +52,12 @@ class ImageField(serializers.ImageField):
             return data
 
 
+class ScriptSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Script
+        fields = '__all__'
+
+
 class PartMoveSerializer(serializers.ModelSerializer):
     index = serializers.IntegerField()
 
@@ -99,6 +106,8 @@ class LineTypeSerializer(serializers.ModelSerializer):
 
 
 class DocumentSerializer(serializers.ModelSerializer):
+    main_script = serializers.SlugRelatedField(slug_field='name',
+                                               queryset=Script.objects.all())
     transcriptions = TranscriptionSerializer(many=True, read_only=True)
     valid_block_types = BlockTypeSerializer(many=True, read_only=True)
     valid_line_types = LineTypeSerializer(many=True, read_only=True)
@@ -106,12 +115,18 @@ class DocumentSerializer(serializers.ModelSerializer):
 
     class Meta:
         model = Document
-        fields = ('pk', 'name', 'transcriptions',
+        fields = ('pk', 'name', 'transcriptions', 'main_script', 'read_direction',
                   'valid_block_types', 'valid_line_types', 'parts_count')
 
     def get_parts_count(self, document):
         return document.parts.count()
 
+    def validate_main_script(self, value):
+        try:
+            return Script.objects.get(name=value)
+        except Script.DoesNotExist:
+            raise serializers.ValidationError('This script does not exists in the database.')
+
 
 class PartSerializer(serializers.ModelSerializer):
     image = ImageField(required=False, thumbnails=['card', 'large'])
@@ -133,7 +148,8 @@ class PartSerializer(serializers.ModelSerializer):
             'workflow',
             'order',
             'recoverable',
-            'transcription_progress'
+            'transcription_progress',
+            'source'
         )
 
     def create(self, data):
diff --git a/app/apps/api/urls.py b/app/apps/api/urls.py
index 5c68c75144c91691322606062c09a3df48437534..928ec985a8265e478e0bdf05e380b396641746cc 100644
--- a/app/apps/api/urls.py
+++ b/app/apps/api/urls.py
@@ -11,9 +11,11 @@ from api.views import (DocumentViewSet,
                        BlockTypeViewSet,
                        LineTypeViewSet,
                        LineTranscriptionViewSet,
+                       ScriptViewSet,
                        OcrModelViewSet)
 
 router = routers.DefaultRouter()
+router.register(r'scripts', ScriptViewSet)
 router.register(r'documents', DocumentViewSet)
 router.register(r'user', UserViewSet)
 router.register(r'types/block', BlockTypeViewSet)
diff --git a/app/apps/api/views.py b/app/apps/api/views.py
index 23722ea8683c7e712fc6e4761100ada5eac108f5..f33ba76d4db9f3d9c3e0acd681d53083e812252e 100644
--- a/app/apps/api/views.py
+++ b/app/apps/api/views.py
@@ -11,7 +11,7 @@ from django.urls import reverse
 from rest_framework.decorators import action
 from rest_framework.response import Response
 from rest_framework import status
-from rest_framework.viewsets import ModelViewSet
+from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
 from rest_framework.pagination import PageNumberPagination
 from rest_framework.serializers import PrimaryKeyRelatedField
 
@@ -31,6 +31,7 @@ from api.serializers import (UserOnboardingSerializer,
                              SegmentSerializer,
                              TrainSerializer,
                              SegTrainSerializer,
+                             ScriptSerializer,
                              TranscribeSerializer,
                              OcrModelSerializer)
 
@@ -43,6 +44,7 @@ from core.models import (Document,
                          Transcription,
                          LineTranscription,
                          OcrModel,
+                         Script,
                          AlreadyProcessingException)
 
 from core.tasks import recalculate_masks
@@ -67,6 +69,12 @@ class UserViewSet(ModelViewSet):
             return Response(status=status.HTTP_200_OK)
 
 
+class ScriptViewSet(ReadOnlyModelViewSet):
+    queryset = Script.objects.all()
+    paginate_by = 20
+    serializer_class = ScriptSerializer
+
+
 class DocumentViewSet(ModelViewSet):
     queryset = Document.objects.all()
     serializer_class = DocumentSerializer
diff --git a/app/apps/core/models.py b/app/apps/core/models.py
index 680f26f313b5a88b18c3f5133f0737c4cf062fef..9299c1bcd024634896fdecc6d9dfc2630aeef831 100644
--- a/app/apps/core/models.py
+++ b/app/apps/core/models.py
@@ -187,7 +187,8 @@ class Document(models.Model):
     read_direction = models.CharField(
         max_length=3,
         choices=READ_DIRECTION_CHOICES,
-        default=READ_DIRECTION_LTR
+        default=READ_DIRECTION_LTR,
+        help_text=_("The read direction describes the order of the elements in the document, in opposition with the text direction which describes the order of the words in a line and is set by the script.")
     )
     typology = models.ForeignKey(DocumentType, null=True, blank=True, on_delete=models.SET_NULL)
 
diff --git a/app/apps/users/urls.py b/app/apps/users/urls.py
index e3aa844ec11047910b94461f1c68f1593fadcdae..fe7530b32fee6445352088bf05dfde2cb79cddc9 100644
--- a/app/apps/users/urls.py
+++ b/app/apps/users/urls.py
@@ -1,7 +1,8 @@
 from django.urls import path, include
 
 from users.views import (SendInvitation, AcceptInvitation, AcceptGroupInvitation, ContactUsView,
-                         ProfileInfos, ProfileGroupListCreate, ProfileApiKey, ProfileFiles,
+                         ProfileInfos, ProfileGroupListCreate, ProfileApiKey,
+                         ProfileFiles, ProfileInvitations,
                          GroupDetail, RemoveFromGroup, LeaveGroup, TransferGroupOwnership)
 from django.contrib.auth.decorators import permission_required
 
@@ -11,6 +12,7 @@ urlpatterns = [
     path('profile/apikey/', ProfileApiKey.as_view(), name='profile-api-key'),
     path('profile/files/', ProfileFiles.as_view(), name='profile-files'),
     path('profile/teams/', ProfileGroupListCreate.as_view(), name='profile-team-list'),
+    path('profile/invitations/', ProfileInvitations.as_view(), name='profile-invites-list'),
     path('teams/<int:pk>/', GroupDetail.as_view(), name='team-detail'),
     path('teams/<int:pk>/remove/', RemoveFromGroup.as_view(), name='team-remove-user'),
     path('teams/<int:pk>/leave/', LeaveGroup.as_view(), name='team-leave'),
diff --git a/app/apps/users/views.py b/app/apps/users/views.py
index 9eb56f23eeca70d38f8f6f06788949cbd5eb7442..03cd6379dd38bf3034fcdabce3255a8d71243bfa 100644
--- a/app/apps/users/views.py
+++ b/app/apps/users/views.py
@@ -189,6 +189,19 @@ class ProfileFiles(LoginRequiredMixin, TemplateView):
         return context
 
 
+class ProfileInvitations(LoginRequiredMixin, TemplateView):
+    template_name = 'users/profile_invitations.html'
+
+    def get_context_data(self, *args, **kwargs):
+        context = super().get_context_data(*args, **kwargs)
+        invites = self.request.user.invitations_sent.all()
+        paginator = Paginator(invites, 25)
+        context['is_paginated'] = paginator.count != 0
+        page_number = self.request.GET.get('page')
+        context['page_obj'] = paginator.get_page(page_number)
+        return context
+
+
 class ProfileGroupListCreate(LoginRequiredMixin, SuccessMessageMixin, CreateView):
     """
     Both were we create new groups and list them
diff --git a/app/escriptorium/templates/core/document_form.html b/app/escriptorium/templates/core/document_form.html
index c6cb28d666d63a1c89d28614d54c6a430688aad3..baaf9abf1a7800e52d5d28bdbb5248ad876f6e8b 100644
--- a/app/escriptorium/templates/core/document_form.html
+++ b/app/escriptorium/templates/core/document_form.html
@@ -1,5 +1,5 @@
 {% extends 'core/document_nav.html' %}
-{% load i18n staticfiles bootstrap %}
+{% load i18n staticfiles bootstrap json %}
 {% block head_title %}{% if object %}{% trans "Update a Document" %}{% else %}{% trans "Create a new Document" %}{% endif %}{% endblock %}
 
 {% block nav-doc-active %}active{% endblock %}
@@ -173,9 +173,11 @@
 </script>
 {{ block.super }}
 <script type="text/javascript">
-    $(document).ready(function(){
-      bootDocumentForm();
-      bootHelp();
-    });
+ $(document).ready(function(){
+     var scripts = { {% for script in form.fields.main_script.queryset %}
+         '{{script.pk}}':'{{script.text_direction}}',{% endfor %} };
+     bootDocumentForm(scripts);
+     bootHelp();
+ });
 </script>
 {% endblock %}
diff --git a/app/escriptorium/templates/export/alto.xml b/app/escriptorium/templates/export/alto.xml
index ed28bb0e81b8cc6b01fdc868ec76cd11740c2e7e..80b0afbab80e820df539b3e032c8a7bbf8b6e665 100644
--- a/app/escriptorium/templates/export/alto.xml
+++ b/app/escriptorium/templates/export/alto.xml
@@ -1,11 +1,12 @@
 {% load export_tags %}<?xml version="1.0" encoding="UTF-8"?>
 <alto xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.loc.gov/standards/alto/ns-v4#"
-      xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v4# https://gitlab.inria.fr/scripta/escriptorium/-/raw/develop/app/escriptorium/static/alto-4-1-baselines.xsd">
+      xsi:schemaLocation="http://www.loc.gov/standards/alto/ns-v4# http://www.loc.gov/standards/alto/v4/alto-4-2.xsd">
   <Description>
     <MeasurementUnit>pixel</MeasurementUnit>
     <sourceImageInformation>
       <fileName>{{ part.filename }}</fileName>
+      {% if part.source %}<fileIdentifier>{{ part.source }}</fileIdentifier>{% endif %}
     </sourceImageInformation>
   </Description>
   {% if valid_block_types or valid_line_types %}
diff --git a/app/escriptorium/templates/export/pagexml.xml b/app/escriptorium/templates/export/pagexml.xml
index f0be96718103b82a48bed29cefaaa2b8f6f4cdb9..73ded4ccf75b09cd5f0d11aecafaf9dffd502ed8 100644
--- a/app/escriptorium/templates/export/pagexml.xml
+++ b/app/escriptorium/templates/export/pagexml.xml
@@ -1,9 +1,10 @@
 {% load export_tags %}<?xml version="1.0" encoding="UTF-8"  standalone="yes"?>
 <PcGts xmlns="http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15 http://schema.primaresearch.org/PAGE/gts/pagecontent/2019-07-15/pagecontent.xsd">
-  <Metadata>
+  <Metadata{% if part.source %} externalRef="{{part.source}}"{% endif %}>
 	<Creator>escriptorium</Creator>
 	<Created>{% current_time %}</Created>
-    <LastChange>{% current_time %}</LastChange>
+        <LastChange>{% current_time %}</LastChange>
+
   </Metadata>
   <Page imageFilename="{{ part.filename }}" imageWidth="{{ part.image.width  }}" imageHeight="{{ part.image.height  }}">
 
diff --git a/app/escriptorium/templates/users/profile.html b/app/escriptorium/templates/users/profile.html
index 7d950acd7edff5bab0ef3acf7f8f1589718b72f6..eb4f04990abeb040adee7c241e796a16b3f976b3 100644
--- a/app/escriptorium/templates/users/profile.html
+++ b/app/escriptorium/templates/users/profile.html
@@ -9,6 +9,7 @@
             <a class="nav-link {% block key-tab-active %}{% endblock %}" id="nav-key-tab"  href="{% url 'profile-api-key' %}" role="tab">{% trans "Api key" %}</a>
             <a class="nav-link {% block files-tab-active %}{% endblock %}" id="nav-files-tab" href="{% url 'profile-files' %}" role="tab">{% trans "Files" %}</a>
             <a class="nav-link {% block team-tab-active %}{% endblock %}" id="nav-infos-tab" href="{% url 'profile-team-list' %}" role="tab">{% trans "Teams" %}</a>
+            <a class="nav-link {% block invites-tab-active %}{% endblock %}" id="nav-infos-tab" href="{% url 'profile-invites-list' %}" role="tab">{% trans "Invitations" %}</a>
         </div>
 
         <div class="col-md-8 tab-content" id="v-pills-tabContent">
diff --git a/app/escriptorium/templates/users/profile_api_key.html b/app/escriptorium/templates/users/profile_api_key.html
index 48e1fe1d5cbb66397545879cc9a0fa2acce3d709..59f1529846168610bf208627c5726f3e28476b4a 100644
--- a/app/escriptorium/templates/users/profile_api_key.html
+++ b/app/escriptorium/templates/users/profile_api_key.html
@@ -3,8 +3,6 @@
 
 {% block infos-tab-active %}{% endblock %}
 {% block key-tab-active %}active{% endblock %}
-{% block files-tab-active %}{% endblock %}
-{% block team-tab-active %}{% endblock %}
 
 {% block tab-content %}
 {% trans "API Authentication Token:" %} {{ api_auth_token.key }}
diff --git a/app/escriptorium/templates/users/profile_files.html b/app/escriptorium/templates/users/profile_files.html
index 9849d42183ac7c6a189a0f0ff225190301ccb432..a4277c5a9b15299ae8087e4768cae7e9779fbafc 100644
--- a/app/escriptorium/templates/users/profile_files.html
+++ b/app/escriptorium/templates/users/profile_files.html
@@ -2,9 +2,7 @@
 {% load i18n static %}
 
 {% block infos-tab-active %}{% endblock %}
-{% block key-tab-active %}{% endblock %}
 {% block files-tab-active %}active{% endblock %}
-{% block team-tab-active %}{% endblock %}
 
 {% block tab-content %}
 {% for fpath, fname in page_obj %}
diff --git a/app/escriptorium/templates/users/profile_group_list.html b/app/escriptorium/templates/users/profile_group_list.html
index 3a7b855f17bd40eab0a437e2a3a1458c85a5ff31..728f47a4b08703bfdba6f47ebee51a410102d9f6 100644
--- a/app/escriptorium/templates/users/profile_group_list.html
+++ b/app/escriptorium/templates/users/profile_group_list.html
@@ -2,12 +2,9 @@
 {% load i18n bootstrap static %}
 
 {% block infos-tab-active %}{% endblock %}
-{% block key-tab-active %}{% endblock %}
-{% block files-tab-active %}{% endblock %}
 {% block team-tab-active %}active{% endblock %}
 
 {% block tab-content %}
-
 <h4>{% trans "Create a new Team" %}</h4>
 <form method="post">
     {% csrf_token %}
diff --git a/app/escriptorium/templates/users/profile_invitations.html b/app/escriptorium/templates/users/profile_invitations.html
new file mode 100644
index 0000000000000000000000000000000000000000..60be1c3a9718c12709ea15767ae84106d7fb1b26
--- /dev/null
+++ b/app/escriptorium/templates/users/profile_invitations.html
@@ -0,0 +1,21 @@
+{% extends "users/profile.html" %}
+{% load i18n static %}
+
+{% block infos-tab-active %}{% endblock %}
+{% block invites-tab-active %}active{% endblock %}
+
+{% block tab-content %}
+<table class="table">
+{% for invite in page_obj %}
+    <tr>
+        <td>{{invite.recipient_email|default:invite.recipient}}</td>
+        <td title="Into group">{{invite.group|default:""}}</td>
+        <td>{{invite.get_workflow_state_display}}</td>
+    </tr>
+{% empty %}
+{% trans "You didn't send any invitations yet." %}
+{% endfor %}
+</table>
+
+{% include "includes/pagination.html" %}
+{% endblock %}
diff --git a/app/requirements.txt b/app/requirements.txt
index 578411a956c52c1b8fe894d5f9f3482d30970226..cb1e62c76e846723fabffbe31e541d9e96e63c70 100644
--- a/app/requirements.txt
+++ b/app/requirements.txt
@@ -12,7 +12,7 @@ django-redis==4.10.0
 psycopg2-binary==2.7.6
 django-ordered-model==3.1.1
 easy-thumbnails==2.5
-git+https://github.com/mittagessen/kraken.git@3.0b23#egg=kraken
+git+https://github.com/mittagessen/kraken.git@3.0b24#egg=kraken
 django-cleanup==5.1.0
 djangorestframework==3.9.2
 drf-nested-routers==0.91
diff --git a/docker-compose.override.yml_example b/docker-compose.override.yml_example
index 706d9693becda40e375477fa80e6643d964c879e..4e5e10e99352c1a29897c9265f9fce7188894196 100644
--- a/docker-compose.override.yml_example
+++ b/docker-compose.override.yml_example
@@ -3,8 +3,6 @@ version: "3.9"
 services:
     ### to customize the homepage, uncomment this
     #app:
-      # environment:
-      #   - CUSTOM_HOME=True
       # volumes:
       #   - $PWD/app/homepage
 
diff --git a/docker-compose.yml b/docker-compose.yml
index f8f155c8789579ec25ded7040881e93d8e578c79..a42c9720634017078f1931cbbca79f0b62c0415b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -18,7 +18,6 @@ services:
     expose:
       - 8000
 
-
   channelserver:
     <<: *app
     command: daphne --bind 0.0.0.0 --port 5000 -v 1 escriptorium.asgi:application
diff --git a/front/src/document_form.js b/front/src/document_form.js
index 20d352c7312ac10395e7f1dae68c0c3bc9f7bab3..a4461987d3cfb63acb7a495ec7596732cfb1c1aa 100644
--- a/front/src/document_form.js
+++ b/front/src/document_form.js
@@ -1,6 +1,6 @@
 'use strict';
 /**** Metadata stuff ****/
-export function bootDocumentForm() {
+export function bootDocumentForm(scripts) {
     // delete a metadata row
     $('.js-metadata-delete').click(function(ev) {
         var btn = ev.target;
@@ -119,4 +119,19 @@ export function bootDocumentForm() {
             window.location.hash = this.hash;
         }
     });
+
+    // When selecting a rtl script, select rtl read direction
+    // flash shortly the read direction to tell the user when it changed
+    $('#id_main_script').on('change', function(ev) {
+        let dir = scripts[ev.target.value];
+        if (dir=='horizontal-rl') {
+            if ($('#id_read_direction').val() !== 'rtl') {
+                $('#id_read_direction').val('rtl').addClass('is-valid').removeClass('is-valid', 1000);
+            }
+        } else {
+            if ($('#id_read_direction').val() !== 'ltr') {
+                $('#id_read_direction').val('ltr').addClass('is-valid').removeClass('is-valid', 1000);
+            }
+        }
+    });
 }
diff --git a/front/src/editor/store/document.js b/front/src/editor/store/document.js
index ee3ffe0a4963cec7ce16f41ce070b43f7b240b5e..d24aac332cc0ddc933b456c30578fdafd4f69b15 100644
--- a/front/src/editor/store/document.js
+++ b/front/src/editor/store/document.js
@@ -14,10 +14,10 @@ export const initialState = () => ({
     // Manage panels visibility through booleans
     // Those values are initially populated by localStorage
     visible_panels: {
-        source: userProfile.get('source-panel'),
-        segmentation: userProfile.get('segmentation-panel'),
-        visualisation: userProfile.get('visualisation-panel'),
-        diplomatic: userProfile.get('diplomatic-panel')
+        source: userProfile.get('visible-panels')?userProfile.get('visible-panels').source:false,
+        segmentation: userProfile.get('visible-panels')?userProfile.get('visible-panels').segmentation:true,
+        visualisation: userProfile.get('visible-panels')?userProfile.get('visible-panels').visualisation:true,
+        diplomatic: userProfile.get('visible-panels')?userProfile.get('visible-panels').diplomatic:false
     },
 })
 
@@ -70,7 +70,7 @@ export const actions = {
         commit('setVisiblePanels', update)
 
         // Persist final value in user profile
-        userProfile.set(panel + '-panel', state.visible_panels[panel])
+        userProfile.set('visible-panels', state.visible_panels)
     }
 }
 
diff --git a/front/vue/components/SegPanel.vue b/front/vue/components/SegPanel.vue
index f8d605c26b3268cfbe86053fd51878c37fecbe95..1538bce2f5c31c1374e82d2c8bc09a5f946c6844 100644
--- a/front/vue/components/SegPanel.vue
+++ b/front/vue/components/SegPanel.vue
@@ -94,7 +94,7 @@
                     class="btn btn-sm btn-info fas fa-question help nav-item ml-2">
             </button>
             <div id="segmentation-help" class="alert alert-primary help-text collapse">
-                <button type="button" class="close" aria-label="Close">
+                <button type="button" data-toggle="collapse" data-target="#segmentation-help" class="close" aria-label="Close">
                     <span aria-hidden="true">&times;</span>
                 </button>
                 <help></help>
diff --git a/front/vue/components/TranscriptionModal.vue b/front/vue/components/TranscriptionModal.vue
index 5cb968f7afa9f6580d7c78900bb27bcfc6a32970..9cfb95a8b41b5cece45d5c526c73168258d4c7bc 100644
--- a/front/vue/components/TranscriptionModal.vue
+++ b/front/vue/components/TranscriptionModal.vue
@@ -10,16 +10,16 @@
                     <button v-if="$store.state.document.readDirection == 'rtl'"
                             type="button"
                             id="next-btn"
-                            @click="$store.dispatch('lines/editLine', 'next')"
-                            title="Next"
+                            @click="editLine('next')"
+                            title="Next (up arrow)"
                             class="btn btn-sm mr-1 btn-secondary">
                         <i class="fas fa-arrow-circle-left"></i>
                     </button>
                     <button v-else
                             type="button"
                             id="prev-btn"
-                            @click="$store.dispatch('lines/editLine', 'previous')"
-                            title="Previous"
+                            @click="editLine('previous')"
+                            title="Previous (up arrow)"
                             class="btn btn-sm mr-1 btn-secondary">
                         <i class="fas fa-arrow-circle-left"></i>
                     </button>
@@ -27,16 +27,16 @@
                     <button v-if="$store.state.document.readDirection == 'rtl'"
                             type="button"
                             id="prev-btn"
-                            @click="$store.dispatch('lines/editLine', 'previous')"
-                            title="Previous"
+                            @click="editLine('previous')"
+                            title="Previous (down arrow)"
                             class="btn btn-sm mr-1 btn-secondary">
                         <i class="fas fa-arrow-circle-right"></i>
                     </button>
                     <button v-else
                             type="button"
                             id="next-btn"
-                            @click="$store.dispatch('lines/editLine', 'next')"
-                            title="Next"
+                            @click="editLine('next')"
+                            title="Next (down arrow)"
                             class="btn btn-sm mr-1 btn-secondary">
                         <i class="fas fa-arrow-circle-right"></i>
                     </button>
@@ -71,9 +71,9 @@
 
                     <div id="trans-input-container" ref="transInputContainer">
                         <input v-if="$store.state.document.mainTextDirection != 'ttb'"
-                                v-on:keyup.down="$store.dispatch('lines/editLine', 'next')"
-                                v-on:keyup.up="$store.dispatch('lines/editLine', 'previous')"
-                                v-on:keyup.enter="$store.dispatch('lines/editLine', 'next')"
+                                v-on:keyup.down="editLine('next')"
+                                v-on:keyup.up="editLine('previous')"
+                                v-on:keyup.enter="editLine('next')"
                                 id="trans-input"
                                 ref="transInput"
                                 name="content"
@@ -83,9 +83,9 @@
                                 autofocus/>
                         <!--Hidden input for ttb text: -->
                         <input v-else
-                                id="trans-input" 
+                                id="trans-input"
                                 ref="transInput"
-                                name="content" 
+                                name="content"
                                 type="hidden"
                                 v-model.lazy="localTranscription"
                                 autocomplete="off" />
@@ -95,9 +95,9 @@
                             <div id="textInputBorderWrapper" class="form-control mb-2">
                                 <div    v-on:blur="localTranscription = $event.target.textContent"
                                         v-on:keyup="recomputeInputCharsScaleY()"
-                                        v-on:keyup.right="$store.dispatch('lines/editLine', 'next')"
-                                        v-on:keyup.left="$store.dispatch('lines/editLine', 'previous')"
-                                        v-on:keyup.enter="cleanHTMLTags();recomputeInputCharsScaleY();$store.dispatch('lines/editLine', 'next')"
+                                        v-on:keyup.right="editLine('next')"
+                                        v-on:keyup.left="editLine('previous')"
+                                        v-on:keyup.enter="cleanHTMLTags();recomputeInputCharsScaleY();editLine('next')"
                                         v-html="localTranscription"
                                         id="vertical_text_input"
                                             contenteditable="true">
@@ -217,8 +217,8 @@ export default Vue.extend({
         // no need to make focus on hiden input with a ttb text
         if(this.$store.state.document.mainTextDirection != 'ttb'){
             input.focus();
-        }else{  // avoid some br or other html tag for a copied text on an editable input div (vertical_text_input): 
-            // 
+        }else{  // avoid some br or other html tag for a copied text on an editable input div (vertical_text_input):
+            //
             document.getElementById("vertical_text_input").addEventListener("paste", function(e) {
 
                 // cancel paste to treat its content before inserting it
@@ -283,11 +283,18 @@ export default Vue.extend({
         close() {
             $(this.$refs.transModal).modal('hide');
         },
+
+        editLine(direction) {
+           // making sure the line is saved (it isn't in case of shortcut usage)
+           this.localTranscription = this.$refs.transInput.value;
+           this.$store.dispatch('lines/editLine', direction);
+        },
+
         cleanHTMLTags(){
             document.getElementById("vertical_text_input").innerHTML = document.getElementById("vertical_text_input").textContent;
         },
         recomputeInputCharsScaleY(){
-            
+
             let inputHeight = document.getElementById("vertical_text_input").clientHeight;
             let wrapperHeight = document.getElementById("textInputBorderWrapper").clientHeight;
             let textScaleY = wrapperHeight / (inputHeight + 10);
@@ -342,7 +349,7 @@ export default Vue.extend({
             // calculate rotation needed to get the line horizontal
             let target_angle = 0;  // all lines should be topologically ltr
             if(this.$store.state.document.mainTextDirection == 'ttb') // add a 90 angle for vertical texts
-                target_angle = 90; 
+                target_angle = 90;
             let angle = target_angle - this.getLineAngle();
 
             // apply it to the polygon and get the resulting bbox
@@ -483,8 +490,8 @@ export default Vue.extend({
                     //document.getElementById('vertical_text_input').style.height = 100/scaleY + '%'; // not needed here
                 } else {
                     verticalTextInput.style.transform = 'none';
-                    verticalTextInput.style.height = modalImgContainer.clientHeight + 'px'; 
-                }  
+                    verticalTextInput.style.height = modalImgContainer.clientHeight + 'px';
+                }
                 textInputWrapper.style.height = modalImgContainer.clientHeight + 'px';
                 // simulate an input field border to fix it to the actual size of the image
                 textInputBorderWrapper.style.width = verticalTextInput.clientWidth+'px';
@@ -506,7 +513,7 @@ export default Vue.extend({
 
             //
             let ratio = 1;
-            let lineHeight = 150; 
+            let lineHeight = 150;
 
             if(this.$store.state.document.mainTextDirection != 'ttb')
             {
@@ -523,9 +530,9 @@ export default Vue.extend({
                 modalImgContainer.style.height=String(window.innerHeight-230) + "px";   //   needed to fix height or ratio is nulled
                 ratio = modalImgContainer.clientHeight / (bbox.height + (2*bbox.width*hContext));
                 let MAX_WIDTH = 30;
-                lineHeight = Math.max(30, Math.round(bbox.width*ratio));   
-                
-                if (lineHeight > MAX_WIDTH) {    
+                lineHeight = Math.max(30, Math.round(bbox.width*ratio));
+
+                if (lineHeight > MAX_WIDTH) {
                     // change the ratio so that the image can not get too big
                     ratio = (MAX_WIDTH/lineHeight)*ratio;
                     lineHeight = MAX_WIDTH;
diff --git a/variables.env_example b/variables.env_example
index 69e9c245e24702af7af536471100947411926c2d..e0b4656925645061a7faa5ac914d4be43a0b0865 100644
--- a/variables.env_example
+++ b/variables.env_example
@@ -20,3 +20,5 @@ FLOWER_BASIC_AUTH=flower:changeme
 
 # set shm_size in yml file!
 KRAKEN_TRAINING_LOAD_THREADS=8
+
+# CUSTOM_HOME=True