diff --git a/CMakeLists.txt b/CMakeLists.txt
index cced477b8003fda0c1099bfa8423001ccc6a7662..3159722c543f7c58d882ba6f9f19b489847e9fb8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,10 +10,6 @@ set(CMAKE_AUTOUIC ON)
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 
-# C++20, at least we try
-set(CMAKE_CXX_STANDARD 20)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
 # Pthread ftw
 set(THREADS_PREFER_PTHREAD_FLAG ON)
 
@@ -59,20 +55,38 @@ target_link_libraries(Vivy PRIVATE lua)
 
 # Headers related things
 target_include_directories(Vivy PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc)
-target_precompile_headers(Vivy
-    PRIVATE
-        # Private Vivy headers
-        ${Vivy_INC}
-
-        # Qt headers
-        <QString>
-        <QList>
-        <QMap>
-        <QWidget>
-        <QIcon>
-
-        # STL headers
-        <memory>
+target_precompile_headers(Vivy PRIVATE
+    # Private Vivy headers
+    ${Vivy_INC}
+
+    # STL headers
+    <memory>
+    <vector>
+
+    # Qt headers
+    <QString>
+    <QList>
+    <QVector>
+    <QMap>
+    <QWidget>
+    <QIcon>
+)
+
+# Set Vivy's needed C++ features
+target_compile_features(Vivy PRIVATE
+    cxx_std_20
+    cxx_auto_type
+    cxx_deleted_functions
+    cxx_explicit_conversions
+    cxx_final
+    cxx_inline_namespaces
+    cxx_lambdas
+    cxx_noexcept
+    cxx_nonstatic_member_init
+    cxx_nullptr
+    cxx_override
+    cxx_range_for
+    cxx_strong_enums
 )
 
 # More options and warnings
@@ -90,7 +104,9 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
         -Wno-unused-private-field   # Skip the unused private fields for now
         -fopenmp                    # We do OpenMP here
     )
-    target_link_libraries(Vivy PRIVATE -fopenmp)
+    target_link_libraries(Vivy PRIVATE
+        -fopenmp
+    )
 elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
     target_compile_options(Vivy PRIVATE -fopenmp)
     target_link_libraries(Vivy PRIVATE -fopenmp)
diff --git a/src/Lib/Ass/Ass.hh b/src/Lib/Ass/Ass.hh
index 59eb171f92087ab6592d1e4d17737a95366733ab..acb14ff2f5c568a0db4277cb455a278526f210d0 100644
--- a/src/Lib/Ass/Ass.hh
+++ b/src/Lib/Ass/Ass.hh
@@ -1,21 +1,12 @@
 #ifndef VIVY_ASS_ASS_H
 #define VIVY_ASS_ASS_H
 
-#include "Char.hh"
 #include "Line.hh"
 #include "Syl.hh"
 #include "Style.hh"
+#include "AssPrivate.hh"
 #include "AssFactory.hh"
 #include "StyleProperties.hh"
 #include <memory.h>
 
-namespace Vivy::Ass
-{
-using StylePtr = AssFactory::StylePtr;
-using LinePtr  = AssFactory::LinePtr;
-
-using StyleWeakPtr = AssFactory::StyleWeakPtr;
-using LineWeakPtr  = AssFactory::LineWeakPtr;
-}
-
 #endif // VIVY_ASS_ASS_H
diff --git a/src/Lib/Ass/AssFactory.cc b/src/Lib/Ass/AssFactory.cc
index 91a61f1c4a311b6feb78c1663c248417bd20f1e0..06bd5db2f487e370ad6d301cd98e96b59fbfae75 100644
--- a/src/Lib/Ass/AssFactory.cc
+++ b/src/Lib/Ass/AssFactory.cc
@@ -1,5 +1,6 @@
 #include "AssFactory.hh"
 
+#include <algorithm>
 #include <stdexcept>
 
 using namespace Vivy::Ass;
@@ -10,17 +11,19 @@ AssFactory::initFromStorage() noexcept
     QTextStream in(&diskStorage);
     QString currentSection{};
     quint64 lineIndex = 0;
+    QStringList stylesContent{};
+    QStringList eventsContent{};
 
     while (!in.atEnd()) {
         const QString line = in.readLine().trimmed();
 
-        /* Dectect comment */
+        // Dectect comment
         if (line.startsWith(";") || line.isEmpty()) {
             lineIndex++;
             continue;
         }
 
-        /* Dectect sections */
+        // Dectect sections
         else if (line.startsWith("[") && line.endsWith("]")) {
             currentSection = line.mid(1, line.size() - 2);
             qDebug() << "Parsing section" << currentSection;
@@ -30,31 +33,45 @@ AssFactory::initFromStorage() noexcept
             }
         }
 
-        /* Other lines */
+        // Other lines
         else if (!currentSection.isEmpty()) {
             const int separatorIndex = line.indexOf(": ");
             const int baseValueIndex = separatorIndex + 2;
-            if (separatorIndex < 0) {
+
+            // Easy way to see if the line was invalid
+            if (separatorIndex < 0)
                 qWarning() << "Invalid line #" << lineIndex << ":" << line;
-            } else {
-                assContent[currentSection].insert(line.mid(0, separatorIndex),
-                                                  line.mid(baseValueIndex));
+
+            // Script's info
+            else if (currentSection == sectionScriptInfo) {
+                assInfo.insert(line.mid(0, separatorIndex), line.mid(baseValueIndex));
                 qDebug() << "Got line #" << lineIndex << ":" << line;
             }
+
+            // Skip the headers and the comment lines
+            else if ((currentSection == sectionStyles) && line.startsWith("Style: ")) {
+                stylesContent.append(line);
+            } else if ((currentSection == sectionEvents) && line.startsWith("Dialogue: ")) {
+                eventsContent.append(line);
+            } else if ((currentSection == sectionEvents) && line.startsWith("Comment: ")) {
+                eventsContent.append(line);
+            }
         }
 
         lineIndex++;
     }
 
-    /* Construct the styles and the lines */
-    for (const auto &styleLine : assContent[sectionStyles])
-        assStyles.push_back(std::make_shared<Style>(styleLine.toString()));
-    for (const auto &assLine : assContent[sectionEvents])
-        assLines.push_back(std::make_shared<Line>(assLine.toString()));
+    // Construct the styles and the lines
+    try {
+        for (const auto &styleLine : stylesContent)
+            assStyles.push_back(std::make_shared<Style>(styleLine));
+        for (const auto &assLine : eventsContent)
+            assLines.push_back(std::make_shared<Line>(this, assLine));
+    } catch (const std::runtime_error &e) {
+        qCritical() << "Failed to create ASS style or events with error:" << e.what();
+    }
 
-    /* Get back some memory */
-    assContent.remove(sectionStyles);
-    assContent.remove(sectionEvents);
+    qDebug() << "Got" << assLines.size() << "ASS dialog lines";
 
     return true;
 }
@@ -62,27 +79,31 @@ AssFactory::initFromStorage() noexcept
 bool
 AssFactory::checkValidity() const noexcept
 {
-    if (assContent.isEmpty()) {
+    if (assInfo.isEmpty()) {
+        qCritical() << "Empty info section";
         return false;
     }
 
-    const SectionContent &scriptInfo = assContent[sectionScriptInfo];
-
-    /* Check for fields that must be integers */
-    bool ok                                  = false;
-    SectionContent::const_iterator it        = scriptInfo.begin();
-    const SectionContent::const_iterator end = scriptInfo.end();
+    // Check for fields that must be integers
+    SectionContent::const_iterator it        = assInfo.begin();
+    const SectionContent::const_iterator end = assInfo.end();
     while (it != end) {
-        if (intTypeFields.contains(it.key()) && (it.value().toInt(&ok), !ok))
+        bool ok = false;
+        if (intTypeFields.contains(it.key()) && (it.value().toInt(&ok), !ok)) {
+            qCritical() << it.key() << "is not an integer:" << it.value();
             return false;
+        }
         ++it;
     }
 
-    /* Check for fixed values fields */
+    // Check for fixed values fields
     for (const auto &fixedValues : checkedValues) {
         if (const auto &first = fixedValues.first;
-            scriptInfo.contains(first) && scriptInfo[first] != fixedValues.second)
+            assInfo.contains(first) && assInfo[first] != fixedValues.second) {
+            qCritical() << "Invalid" << first << "as it should be equal to" << fixedValues.second
+                        << "but was" << assInfo[first];
             return false;
+        }
     }
 
     return true;
@@ -102,7 +123,7 @@ AssFactory::AssFactory(const QString &fileName)
 }
 
 void
-AssFactory::getStyles(QVector<AssFactory::StylePtr> &ret) const noexcept
+AssFactory::getStyles(QVector<StylePtr> &ret) const noexcept
 {
     ret.clear();
     for (const auto &style : assStyles)
@@ -110,7 +131,7 @@ AssFactory::getStyles(QVector<AssFactory::StylePtr> &ret) const noexcept
 }
 
 void
-AssFactory::getLines(QVector<AssFactory::LinePtr> &ret) const noexcept
+AssFactory::getLines(QVector<LinePtr> &ret) const noexcept
 {
     ret.clear();
     for (const auto &line : assLines)
@@ -120,5 +141,30 @@ AssFactory::getLines(QVector<AssFactory::LinePtr> &ret) const noexcept
 AssFactory::SectionContent
 AssFactory::getInfoSection() const noexcept
 {
-    return assContent[sectionScriptInfo];
+    return assInfo;
+}
+
+StyleWeakPtr
+AssFactory::getStyle(const QString &name) const noexcept
+{
+    auto findByName = [&name](const StylePtr &style) noexcept -> bool {
+        return style->getElementName() == name;
+    };
+
+    auto styleIt = std::find_if(std::begin(assStyles), std::end(assStyles), findByName);
+    if (styleIt != std::end(assStyles))
+        return StyleWeakPtr(*styleIt);
+
+    // Will be unable to lock
+    return StyleWeakPtr(spareNullStylePtr);
+}
+
+bool
+AssFactory::hasStyle(const QString &name) const noexcept
+{
+    for (const auto &stylePtr : assStyles) {
+        if (stylePtr->getElementName() == name)
+            return true;
+    }
+    return false;
 }
diff --git a/src/Lib/Ass/AssFactory.hh b/src/Lib/Ass/AssFactory.hh
index 48f849b01cc6ef60afbaa5b4d18627b59d24f439..f21fddb15a6b33a60917b0eb0371b4cd33592599 100644
--- a/src/Lib/Ass/AssFactory.hh
+++ b/src/Lib/Ass/AssFactory.hh
@@ -4,16 +4,16 @@
 #include "../Utils.hh"
 #include "Style.hh"
 #include "Line.hh"
+#include "AssPrivate.hh"
 
-#include <memory.h>
+#include <memory>
 #include <QFile>
 #include <QMap>
 #include <QVariant>
 #include <QString>
 
-/* An ASS file is basically an INI file, but some keys are present multiple
- * times (V4+ Styles/Style, Events/Dialogue, Events/Comment).
- */
+// An ASS file is basically an INI file, but some keys are present multiple
+// times (V4+ Styles/Style, Events/Dialogue, Events/Comment).
 
 namespace Vivy::Ass
 {
@@ -27,29 +27,27 @@ public:
         Events     = 3,
     };
 
-    using LinePtr        = std::shared_ptr<Line>;
-    using LineWeakPtr    = std::weak_ptr<Line>;
-    using StylePtr       = std::shared_ptr<Style>;
-    using StyleWeakPtr   = std::weak_ptr<Style>;
     using SectionContent = QMap<QString, QVariant>;
 
 private:
     QFile diskStorage;
-    QMap<QString, SectionContent> assContent{};
+    SectionContent assInfo{};
     QVector<LinePtr> assLines{};
     QVector<StylePtr> assStyles{};
 
+    StylePtr spareNullStylePtr{ nullptr };
+
     static inline const QString sectionScriptInfo = "Script Info";
     static inline const QString sectionStyles     = "V4+ Styles";
     static inline const QString sectionEvents     = "Events";
     static inline const QStringList validSections = { sectionEvents, sectionScriptInfo,
                                                       sectionStyles };
 
-    static inline const QStringList intTypeFields = { "PlayResX", "PlayResY", "WrapStyle" };
-    static inline const QList<QPair<QString, QString>> checkedValues = {
+    static inline const QStringList intTypeFields{ "PlayResX", "PlayResY", "WrapStyle" };
+    static inline const QVector<QPair<QString, QString>> checkedValues{
         { "ScriptType", "v4.00+" },
         { "WrapStyle", "0" },
-        { "YCbCr Matrix", "TV.601" },
+        // { "YCbCr Matrix", "TV.601" },
         { "ScaledBorderAndShadow", "yes" }
     };
 
@@ -60,6 +58,9 @@ public:
     explicit AssFactory(const QString &);
     ~AssFactory() noexcept = default;
 
+    bool hasStyle(const QString &) const noexcept;
+    StyleWeakPtr getStyle(const QString &) const noexcept;
+
     SectionContent getInfoSection() const noexcept;
     void getStyles(QVector<StylePtr> &) const noexcept;
     void getLines(QVector<LinePtr> &) const noexcept;
diff --git a/src/Lib/Ass/AssPrivate.hh b/src/Lib/Ass/AssPrivate.hh
new file mode 100644
index 0000000000000000000000000000000000000000..71f9c7ee1b3fcfddb33e7961a01d7a96ab39bc1d
--- /dev/null
+++ b/src/Lib/Ass/AssPrivate.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <memory>
+
+namespace Vivy::Ass
+{
+class Style;
+class Line;
+
+using StylePtr = std::shared_ptr<Style>;
+using LinePtr  = std::shared_ptr<Line>;
+
+using StyleWeakPtr = std::weak_ptr<Style>;
+using LineWeakPtr  = std::weak_ptr<Line>;
+}
diff --git a/src/Lib/Ass/Char.cc b/src/Lib/Ass/Char.cc
deleted file mode 100644
index e768c6bbf4e3552c3aecdd0538871a1d6c586d96..0000000000000000000000000000000000000000
--- a/src/Lib/Ass/Char.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "Char.hh"
-#include "Syl.hh"
-
-using namespace Vivy::Ass;
-
-Char::Char(Syl *const syl, const QChar /*unused*/)
-    : parentLine(syl->parentLine)
-    , parentSyl(syl)
-{
-}
diff --git a/src/Lib/Ass/Char.hh b/src/Lib/Ass/Char.hh
deleted file mode 100644
index a632e341ef1c2b5dcbfaddad4fd537af7c4b2b91..0000000000000000000000000000000000000000
--- a/src/Lib/Ass/Char.hh
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef VIVY_ASS_CHAR_H
-#define VIVY_ASS_CHAR_H
-
-#include <QChar>
-#include <QtGlobal>
-#include "StyleProperties.hh"
-
-namespace Vivy::Ass
-{
-class Line;
-class Syl;
-
-class Char {
-private:
-    QChar content{};
-    StyleProperties styleProperties;
-    quint64 dur{ 0 };
-
-public:
-    Line *const parentLine;
-    Syl *const parentSyl;
-
-public:
-    // Copy constructor
-    explicit Char(const Char &) = default;
-    explicit Char(Syl *const, const QChar);
-
-    Char &operator=(const Char &) = delete;
-    ~Char() noexcept              = default;
-};
-}
-
-#endif // VIVY_ASS_CHAR_H
diff --git a/src/Lib/Ass/Line.cc b/src/Lib/Ass/Line.cc
index 1b7b8c6a21de97616d506a86d87aff64a9832b0a..7fabdbace9fdd01450f41f49c1f295b91ff5e88b 100644
--- a/src/Lib/Ass/Line.cc
+++ b/src/Lib/Ass/Line.cc
@@ -1,9 +1,93 @@
 #include "Line.hh"
+#include "AssFactory.hh"
+
+#include <QRegularExpression>
 
 using namespace Vivy::Ass;
 
-Line::Line(const QString & /*unused*/)
+Line::Line(AssFactory *const factory, const QString &lineString)
+    : assFactory(factory)
+{
+    enum LineIndex : int {
+        Layer   = 0, // int
+        Start   = 1, // time
+        End     = 2, // time
+        Style   = 3, // text
+        Name    = 4, // text
+        MarginL = 5, // int
+        MarginR = 6, // int
+        MarginV = 7, // int
+        Effect  = 8, // text
+        Text    = 9, // text
+        PastLastCode
+
+        // NOTE: time is of the form: `h:mm:ss.cc`
+    };
+
+    static const QString lineHeader = "Dialogue: ";
+    isComment                       = !lineString.startsWith(lineHeader);
+
+    const QString lineContent = lineString.mid(lineString.indexOf(": ") + 2 /* strlen ": " */);
+    QStringList contentList   = lineContent.split(",", Qt::KeepEmptyParts, Qt::CaseInsensitive);
+
+    if (contentList.size() < LineIndex::PastLastCode)
+        throw std::runtime_error(("invalid number of items " + QString::number(contentList.size()) +
+                                  " instead of something superiror or equal to " +
+                                  QString::number(PastLastCode) + " for line: " + lineContent)
+                                     .toStdString());
+
+    layer  = Utils::decodeLineToInteger(contentList[LineIndex::Layer], "Layer is not an integer");
+    effect = contentList[LineIndex::Effect];
+    nameOrActor = contentList[LineIndex::Name];
+    start       = Utils::Time::fromString(contentList[LineIndex::Start]).toUInt();
+    end         = Utils::Time::fromString(contentList[LineIndex::End]).toUInt();
+
+    styleProperties.marginL =
+        Utils::decodeLineToInteger(contentList[LineIndex::MarginL], "MarginL is not an integer");
+    styleProperties.marginR =
+        Utils::decodeLineToInteger(contentList[LineIndex::MarginR], "MarginR is not an integer");
+    styleProperties.marginV =
+        Utils::decodeLineToInteger(contentList[LineIndex::MarginV], "MarginV is not an integer");
+
+    const QString style = contentList[LineIndex::Style];
+    if (!assFactory->hasStyle(style))
+        throw std::runtime_error(("Invalid or not declared style name: " + style).toStdString());
+    lineStyle = assFactory->getStyle(style);
+
+    // Pop all but the text, it may contains `,` characters
+    for (int i = 0; i < LineIndex::Text; ++i)
+        contentList.removeFirst();
+    ___contentAsText = contentList.join("");
+    initSylFromString(___contentAsText);
+}
+
+void
+Line::initSylFromString(const QString &line) noexcept
 {
+    // Matches syllabes like: `{\toto}{\alpha&HFF}content`
+    QRegularExpression re("((?:{[^}]*})+[^{]*)");
+    if (!re.isValid())
+        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
+
+    bool once = false;
+    try {
+        QRegularExpressionMatchIterator it = re.globalMatch(line);
+
+        while (it.hasNext()) {
+            QRegularExpressionMatch match = it.next();
+            content.append(Syl(this, match.captured(1)));
+            once |= true;
+        }
+    } catch (const std::runtime_error &e) {
+        qCritical() << "Failed to init syllabes with line:" << line;
+        qCritical() << "Fallback to all line is one syllabe";
+        once = false;
+    }
+
+    if (!once) {
+        content.clear();
+        content.append(Syl(this, line, Syl::ConstructMode::Raw));
+    }
 }
 
 void
@@ -21,3 +105,33 @@ Line::setEnd(quint64 s) noexcept
     if (start > s)
         start = s;
 }
+
+quint64
+Line::getDuration() const noexcept
+{
+    return end - start;
+}
+
+StyleProperties
+Line::getStyleProperties() const noexcept
+{
+    return styleProperties;
+}
+
+StyleWeakPtr
+Line::getStyle() const noexcept
+{
+    return lineStyle;
+}
+
+const QVector<Syl> &
+Line::getContent() const noexcept
+{
+    return content;
+}
+
+bool
+Line::getIsComment() const noexcept
+{
+    return isComment;
+}
diff --git a/src/Lib/Ass/Line.hh b/src/Lib/Ass/Line.hh
index 7f7bb0511588cc7bdb03c8c7684722ff2a9e536d..7463dad6692230fe9627ff83f21d4f32290f1862 100644
--- a/src/Lib/Ass/Line.hh
+++ b/src/Lib/Ass/Line.hh
@@ -5,27 +5,47 @@
 #include <QtGlobal>
 #include "Syl.hh"
 #include "StyleProperties.hh"
+#include "Style.hh"
 
 namespace Vivy::Ass
 {
-class Line {
+class AssFactory;
+
+class Line final {
 private:
     quint64 start{ 0 };
     quint64 end{ 0 };
+    int layer{ 0 };
+    bool isComment{ true };
 
     QVector<Syl> content{};
-    StyleProperties styleProperties;
+    StyleProperties styleProperties{};
+    QString effect{};
+    QString nameOrActor{};
+    StyleWeakPtr lineStyle;
+
+    QString ___contentAsText;
+
+    AssFactory *const assFactory;
 
 public:
-    explicit Line()             = default;
     explicit Line(const Line &) = default;
-    explicit Line(const QString &);
+    explicit Line(AssFactory *const, const QString &);
     Line &operator=(const Line &) = delete;
 
     ~Line() noexcept = default;
 
     void setStart(quint64 s) noexcept;
     void setEnd(quint64 s) noexcept;
+    quint64 getDuration() const noexcept;
+    bool getIsComment() const noexcept;
+
+    StyleProperties getStyleProperties() const noexcept;
+    StyleWeakPtr getStyle() const noexcept;
+    const QVector<Syl> &getContent() const noexcept;
+
+private:
+    void initSylFromString(const QString &) noexcept;
 };
 
 }
diff --git a/src/Lib/Ass/Style.cc b/src/Lib/Ass/Style.cc
index 58d247a43074935f369c7c469fdb524255d052a7..80d5eb3183194fbd54272dff550a3aa12f8648f0 100644
--- a/src/Lib/Ass/Style.cc
+++ b/src/Lib/Ass/Style.cc
@@ -1,12 +1,14 @@
 #include "Style.hh"
 
 #include <stdexcept>
+#include <QJsonDocument>
+#include <QJsonObject>
 
 using namespace Vivy::Ass;
 
 Style::Style(const QString &styleString)
 {
-    /* Some usefull defines only needed in that function */
+    // Some usefull defines only needed in that function
 
     enum StyleIndex : int {
         Name     = 0,
@@ -34,111 +36,104 @@ Style::Style(const QString &styleString)
         Shadow  = 17,
 
         Alignement = 18,
-        MarginL    = 18,
-        MarginR    = 19,
-        MarginV    = 20,
+        MarginL    = 19,
+        MarginR    = 20,
+        MarginV    = 21,
 
-        Encoding = 21,
+        Encoding = 22,
 
         PastLastCode
     };
 
-    static const QString lineHeader           = "Style: ";
-    static const QString headerSeparator      = ": ";
-    static constexpr int styleContentListSize = StyleIndex::PastLastCode;
+    static const QString lineHeader = "Style: ";
 
-    /* Check line header and content items number */
+    // Check line header and content items number
 
     if (!styleString.startsWith(lineHeader))
-        throw std::runtime_error("invalid style line header");
+        throw std::runtime_error(("invalid style line header: " + styleString).toStdString());
 
-    const QString lineContent = styleString.section(headerSeparator, 2, 2);
+    const QString lineContent = styleString.mid(styleString.indexOf(": ") + 2 /* strlen ": " */);
     const QStringList content = lineContent.split(",", Qt::KeepEmptyParts, Qt::CaseInsensitive);
 
-    if (lineContent.size() != styleContentListSize)
-        throw std::runtime_error("invalid line content: invalid number of intems");
+    if (content.size() != StyleIndex::PastLastCode)
+        throw std::runtime_error(("invalid number of items " + QString::number(content.size()) +
+                                  " instead of " + QString::number(PastLastCode) +
+                                  " for line: " + lineContent)
+                                     .toStdString());
 
-    /* Unpack data from the line */
+    // Unpack data from the line
 
     styleName = content[StyleIndex::Name];
     fontName  = content[StyleIndex::FontName];
-    fontSize  = decodeLineToInteger(content[StyleIndex::FontSize], "FontSize is not an integer");
+    fontSize =
+        Utils::decodeLineToInteger(content[StyleIndex::FontSize], "FontSize is not an integer");
 
     primaryColor   = Color::fromString(content[StyleIndex::ColorPrimary]);
     secondaryColor = Color::fromString(content[StyleIndex::ColorSecondary]);
     outlineColor   = Color::fromString(content[StyleIndex::ColorOutline]);
     backColor      = Color::fromString(content[StyleIndex::ColorBack]);
 
-    bold      = decodeLineToBoolean(content[StyleIndex::Bold], "Bold is not a boolean");
-    italic    = decodeLineToBoolean(content[StyleIndex::Italic], "Italic is not a boolean");
-    underline = decodeLineToBoolean(content[StyleIndex::Underline], "Underline is not a boolean");
-    strikeOut = decodeLineToBoolean(content[StyleIndex::StrikeOut], "StrikeOut is not a boolean");
-
-    scaleX  = decodeLineToFloating(content[StyleIndex::ScaleX], "ScaleX is not a floating");
-    scaleY  = decodeLineToFloating(content[StyleIndex::ScaleY], "ScaleY is not a floating");
-    spacing = decodeLineToFloating(content[StyleIndex::ScaleY], "Spacing is not a floating");
-    angle   = decodeLineToFloating(content[StyleIndex::Angle], "Angle is not a floating");
-    borderStyle =
-        decodeLineToFloating(content[StyleIndex::BorderStyle], "BorderStyle is not a floating");
-    outline = decodeLineToFloating(content[StyleIndex::Outline], "Outline is not a floating");
-    shadow  = decodeLineToFloating(content[StyleIndex::Shadow], "Shadow is not a floating");
-
-    alignment = decodeLineToInteger(content[StyleIndex::Shadow], "Alignement is not an integer");
+    bold   = Utils::decodeLineToBoolean(content[StyleIndex::Bold], "Bold is not a boolean");
+    italic = Utils::decodeLineToBoolean(content[StyleIndex::Italic], "Italic is not a boolean");
+    underline =
+        Utils::decodeLineToBoolean(content[StyleIndex::Underline], "Underline is not a boolean");
+    strikeOut =
+        Utils::decodeLineToBoolean(content[StyleIndex::StrikeOut], "StrikeOut is not a boolean");
+
+    scaleX  = Utils::decodeLineToFloating(content[StyleIndex::ScaleX], "ScaleX is not a floating");
+    scaleY  = Utils::decodeLineToFloating(content[StyleIndex::ScaleY], "ScaleY is not a floating");
+    spacing = Utils::decodeLineToFloating(content[StyleIndex::ScaleY], "Spacing is not a floating");
+    angle   = Utils::decodeLineToFloating(content[StyleIndex::Angle], "Angle is not a floating");
+    borderStyle = Utils::decodeLineToFloating(content[StyleIndex::BorderStyle],
+                                              "BorderStyle is not a floating");
+    outline =
+        Utils::decodeLineToFloating(content[StyleIndex::Outline], "Outline is not a floating");
+    shadow = Utils::decodeLineToFloating(content[StyleIndex::Shadow], "Shadow is not a floating");
+
+    alignment =
+        Utils::decodeLineToInteger(content[StyleIndex::Alignement], "Alignement is not an integer");
     if (alignment < 1 || alignment > 9)
         throw std::runtime_error(
-            "invalid line content: Alignement must be between 1 and 9 included");
+            ("invalid line content: Alignement must be between 1 and 9 included, it was " +
+             QString::number(alignment))
+                .toStdString());
 
-    marginL  = decodeLineToInteger(content[StyleIndex::MarginL], "MarginL is not an integer");
-    marginR  = decodeLineToInteger(content[StyleIndex::MarginR], "MarginR is not an integer");
-    marginV  = decodeLineToInteger(content[StyleIndex::MarginV], "MarginV is not an integer");
-    encoding = decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
+    marginL = Utils::decodeLineToInteger(content[StyleIndex::MarginL], "MarginL is not an integer");
+    marginR = Utils::decodeLineToInteger(content[StyleIndex::MarginR], "MarginR is not an integer");
+    marginV = Utils::decodeLineToInteger(content[StyleIndex::MarginV], "MarginV is not an integer");
+    encoding =
+        Utils::decodeLineToInteger(content[StyleIndex::Encoding], "Encoding is not an integer");
 
     if (encoding != 1)
         qWarning() << "Encoding is not '1' in the ASS Style";
 }
 
-bool
-Style::decodeLineToBoolean(const QString &item, const QString &error)
+QString
+Color::toString(const QColor &c) noexcept
 {
-    return decodeLineToInteger(item, error) >= 1;
-}
-
-int
-Style::decodeLineToInteger(const QString &item, const QString &error)
-{
-    bool conversion = false;
-    int ret         = item.toInt(&conversion);
-    if (!conversion)
-        throw std::runtime_error(("invalid line content: " + error).toStdString());
-    return ret;
-}
-
-float
-Style::decodeLineToFloating(const QString &item, const QString &error)
-{
-    bool conversion = false;
-    float ret       = item.toFloat(&conversion);
-    if (!conversion)
-        throw std::runtime_error(("invalid line content: " + error).toStdString());
-    return ret;
+    return "&H" + QString::number(c.blue(), 16).toUpper() +
+           QString::number(c.green(), 16).toUpper() + QString::number(c.red(), 16).toUpper();
 }
 
 QColor
 Color::fromString(const QString &colorString) noexcept
 {
-    /* Ignore the '&H' at the begeing of the color if needed */
+    // Ignore the '&H' at the begeing of the color if needed
     int startIndex = 0;
-    if (colorString.startsWith("&"))
+    if (colorString[startIndex] == '&')
         startIndex++;
-    if (colorString.startsWith("H") || colorString.startsWith("h"))
+    if (colorString[startIndex] == 'H' || colorString[startIndex] == 'h')
         startIndex++;
 
-    /* In some cases, the color can begin by a '#' */
-    if (colorString.startsWith("#"))
+    // In some cases, the color can begin by a '#'
+    if (colorString[startIndex] == '#')
         startIndex++;
 
-    /* A valid string color is like 'AARRGGBB' for now (skipped 'aH') */
+    // A valid string color is like 'AARRGGBB' for now (skipped 'aH')
     if (colorString.size() - startIndex != 8) {
+        qCritical()
+            << "Invalid color string: size - index_start =" << (colorString.size() - startIndex)
+            << "| string =" << colorString.mid(startIndex);
         qCritical() << "Found an invalid color string:" << colorString;
         return Color::defaultValue;
     }
@@ -159,3 +154,54 @@ Color::fromString(const QString &colorString) noexcept
 
     return QColor(red, green, blue, alpha);
 }
+
+QString
+Style::getElementName() const noexcept
+{
+    return styleName;
+}
+
+QJsonDocument
+Style::getProperties() const noexcept
+{
+    QJsonDocument ret;
+
+    QJsonObject styleFont{
+        { "Font name", fontName }, { "Font size", fontSize },  { "Bold", bold },
+        { "Italic", italic },      { "Underline", underline }, { "Strike Out", strikeOut },
+    };
+
+    QJsonObject styleColors{
+        { "Primary", Color::toString(primaryColor) },
+        { "Secondary", Color::toString(secondaryColor) },
+        { "Outline", Color::toString(outlineColor) },
+        { "Back", Color::toString(backColor) },
+    };
+
+    QJsonObject styleFontProperties{
+        { "Scale X", static_cast<const double>(scaleX) },
+        { "Scale Y", static_cast<const double>(scaleY) },
+        { "Spacing", static_cast<const double>(spacing) },
+        { "Angle", static_cast<const double>(angle) },
+        { "Border Style", static_cast<const double>(borderStyle) },
+        { "Outline", static_cast<const double>(outline) },
+        { "Shadow", static_cast<const double>(shadow) },
+    };
+
+    QJsonObject styleMargins{
+        { "Left", marginL },
+        { "Right", marginR },
+        { "Vertical", marginV },
+    };
+
+    QJsonObject object{ { "Name", styleName },
+                        { "Font", styleFont },
+                        { "Font properties", styleFontProperties },
+                        { "Colors", styleColors },
+                        { "Margin", styleMargins },
+                        { "Alignement", alignment },
+                        { "Encoding", encoding } };
+
+    ret.setObject(object);
+    return ret;
+}
diff --git a/src/Lib/Ass/Style.hh b/src/Lib/Ass/Style.hh
index 21ca04d27a99099562cbfef7f923fc68d19c16e1..9b9bba686f6e61e10b8227a791c73ef288f6e2fc 100644
--- a/src/Lib/Ass/Style.hh
+++ b/src/Lib/Ass/Style.hh
@@ -5,12 +5,15 @@
 #include <QVector>
 #include <QtGlobal>
 #include <QColor>
+#include <QObject>
+#include "AssPrivate.hh"
 
 namespace Vivy::Ass
 {
 namespace Color
 {
 QColor fromString(const QString &) noexcept;
+QString toString(const QColor &) noexcept;
 static inline const QColor defaultValue = QColor(0, 0, 0, 0);
 };
 
@@ -41,10 +44,8 @@ public:
     Style &operator=(const Style &) = delete;
     ~Style() noexcept               = default;
 
-private:
-    static bool decodeLineToBoolean(const QString &item, const QString &error);
-    static int decodeLineToInteger(const QString &item, const QString &error);
-    static float decodeLineToFloating(const QString &item, const QString &error);
+    QString getElementName() const noexcept;
+    QJsonDocument getProperties() const noexcept;
 };
 
 }
diff --git a/src/Lib/Ass/StyleProperties.hh b/src/Lib/Ass/StyleProperties.hh
index d6d5c722ff8871c5437ae17e3580b8e1bb85da1e..374d9e2afb3a42f4c5d195c33fafe5a1494f09c8 100644
--- a/src/Lib/Ass/StyleProperties.hh
+++ b/src/Lib/Ass/StyleProperties.hh
@@ -7,16 +7,15 @@
 
 namespace Vivy::Ass
 {
-/* Overrides some properties of the Style of an Ass::Line, Ass::Char, Ass::Syl
- */
+// Overrides some properties of the Style of an Ass::Line, Ass::Char, Ass::Syl
 
 struct StyleProperties final {
     // Colors
     QColor primaryColor{ Color::defaultValue }, secondaryColor{ Color::defaultValue },
         outlineColor{ Color::defaultValue }, backColor{ Color::defaultValue };
 
-    QString fontName; // Can override the font's name
-    int fontSize{};   // The font size != the font scaling
+    QString fontName{}; // Can override the font's name
+    int fontSize{};     // The font size != the font scaling
 
     bool bold{ false }, italic{ false }, underline{ false }, strikeOut{ false };
 
diff --git a/src/Lib/Ass/Syl.cc b/src/Lib/Ass/Syl.cc
index 5f1bb9612cadb8bd2cfa78a58439c0072e816784..bac0a4d0363e269ec1d984466a767881c53108a6 100644
--- a/src/Lib/Ass/Syl.cc
+++ b/src/Lib/Ass/Syl.cc
@@ -1,8 +1,42 @@
 #include "Syl.hh"
+#include "Line.hh"
+
+#include <QRegularExpression>
 
 using namespace Vivy::Ass;
 
-Syl::Syl(Line *const line, const QString & /*unused*/)
-    : parentLine(line)
+Syl::Syl(Line *const line, const QString &lineString, ConstructMode mode) noexcept
+    : content(lineString)
+    , styleProperties(line->getStyleProperties())
+    , duration(line->getDuration())
+    , parentLine(line)
+{
+    // Will override the `content`, but will be heavy anyway
+    if (mode == ConstructMode::ReadAssTags) {
+        const int textBegin = lineString.lastIndexOf('}') + 1;
+        content             = (textBegin >= lineString.size()) ? "" : lineString.mid(textBegin);
+        duration            = getDurationFromString(lineString);
+    }
+}
+
+quint64
+Syl::getDurationFromString(const QString &line) noexcept
+{
+    QRegularExpression re("\\\\(?:k|K|ko|kf)(\\d+)");
+    if (!re.isValid())
+        qFatal("The regex '%s' is not valid...", re.pattern().toStdString().c_str());
+
+    quint64 duration                   = 0;
+    QRegularExpressionMatchIterator it = re.globalMatch(line);
+
+    while (it.hasNext())
+        duration += it.next().captured(1).toUInt();
+
+    return duration;
+}
+
+QString
+Syl::getContent() const noexcept
 {
+    return content;
 }
diff --git a/src/Lib/Ass/Syl.hh b/src/Lib/Ass/Syl.hh
index cf637f06b66240c89315344295868ded662822f5..79c7a9bc376c028ff9b283d7d12aca3ece0414c0 100644
--- a/src/Lib/Ass/Syl.hh
+++ b/src/Lib/Ass/Syl.hh
@@ -1,7 +1,6 @@
 #ifndef VIVY_SYL_H
 #define VIVY_SYL_H
 
-#include "Char.hh"
 #include "StyleProperties.hh"
 #include <QString>
 #include <QVector>
@@ -11,21 +10,32 @@ namespace Vivy::Ass
 {
 class Line;
 
-class Syl {
+class Syl final {
 private:
-    QVector<Char> content;
+    QString content;
     StyleProperties styleProperties;
-    quint64 dur{ 0 };
+    quint64 duration{ 0 };
 
 public:
     Line *const parentLine;
 
 public:
-    explicit Syl(const Syl &) = default;
-    explicit Syl(Line *const, const QString &);
+    enum class ConstructMode {
+        Raw,         // Don't read ASS tags
+        ReadAssTags, // Read ass tags
+    };
+
+    explicit Syl(const Syl &) noexcept = default;
+    explicit Syl(Line *const, const QString &,
+                 ConstructMode mode = ConstructMode::ReadAssTags) noexcept;
 
     Syl &operator=(const Syl &) = delete;
     ~Syl() noexcept             = default;
+
+    QString getContent() const noexcept;
+
+private:
+    static quint64 getDurationFromString(const QString &) noexcept;
 };
 
 }
diff --git a/src/Lib/Audio.cc b/src/Lib/Audio.cc
index ec20ea67aa8af5be2de9650007cabfa7976cf1e9..e0d7b4808b7be922ebbb34c5410cfd377716cd4c 100644
--- a/src/Lib/Audio.cc
+++ b/src/Lib/Audio.cc
@@ -34,7 +34,6 @@ AudioContext::AudioContext(const QString &path)
         AVCodecParameters *params = itFormat->codecpar;
         AVCodec *streamCodec      = avcodec_find_decoder(params->codec_id);
         if (streamCodec && streamCodec->type == AVMEDIA_TYPE_AUDIO) {
-            audioStreamIndexes.push_back(i);
             audioStreams.insert(i, std::make_shared<Stream>(streamCodec, formatPtr, itFormat, i));
         }
     }
@@ -44,15 +43,12 @@ AudioContext::AudioContext(const QString &path)
                                              -1, // Let AV find one stream
                                              -1, // We don't want related streams
                                              nullptr, 0);
+    if (defaultStreamIndex < 0) {
+        qCritical() << "Could not find the best audio stream";
+    }
 
-    qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration;
-}
-
-// Get the number of audio streams in the audio context
-int
-AudioContext::getStreamCount() const noexcept
-{
-    return audioStreamIndexes.size();
+    qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration
+             << "and default stream index" << defaultStreamIndex;
 }
 
 // Get a specific audio stream, try to lock the weak pointer. if the index was
@@ -60,15 +56,14 @@ AudioContext::getStreamCount() const noexcept
 AudioContext::StreamWeakPtr
 AudioContext::getStream(int index) const noexcept
 {
-    if (index >= audioStreamIndexes.size())
+    if (index < 0)
         return StreamWeakPtr{ spareNullSreamPtr };
 
-    const uint streamIndex = audioStreamIndexes[index];
-    const auto found       = audioStreams.find(streamIndex);
+    uint unsignedIndex = static_cast<uint>(index);
+    const auto found   = audioStreams.find(unsignedIndex);
 
-    if (found != audioStreams.end()) {
+    if (found != audioStreams.end())
         return StreamWeakPtr{ *found };
-    }
 
     return StreamWeakPtr{ spareNullSreamPtr };
 }
@@ -94,7 +89,6 @@ QJsonDocument
 AudioContext::getProperties() const noexcept
 {
     QJsonDocument ret;
-    QJsonObject self;
     QJsonArray streams;
 
     QFileInfo file(filePath);
@@ -103,9 +97,7 @@ AudioContext::getProperties() const noexcept
         streams.append(audioStreamPtr->getProperties());
     }
 
-    self.insert("Streams", streams);
-    self.insert("File path", filePath);
-    self.insert("Base name", file.baseName());
+    QJsonObject self{ { "Streams", streams }, { "Base name", file.baseName() } };
 
     ret.setObject(self);
     return ret;
@@ -157,8 +149,11 @@ AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *format, AVSt
 
 AudioContext::Stream::~Stream() noexcept
 {
-    if (dataPtr)
+    if (dataPtr) {
+        qDebug() << "Free data ptr";
         free(dataPtr);
+    }
+    qDebug() << "Delete stream object";
 }
 
 QJsonObject
diff --git a/src/Lib/Audio.hh b/src/Lib/Audio.hh
index 72b67d288ea24fe93cf160e9f244ee4b7f733a75..3a7ad817e1a6c65d76b36c7084b8098d1e6f63eb 100644
--- a/src/Lib/Audio.hh
+++ b/src/Lib/Audio.hh
@@ -127,7 +127,6 @@ public:
     // The stream non-owning view pointer
     using StreamWeakPtr = std::weak_ptr<Stream>;
 
-    int getStreamCount() const noexcept;
     StreamWeakPtr getStream(int) const noexcept;
     StreamWeakPtr getDefaultStream() const noexcept;
 
@@ -145,7 +144,6 @@ private:
     AVFormatContextPtr format{ avformat_alloc_context(), avFormatContextDeleter };
 
     QString filePath;                     // Usefull information
-    QVector<uint> audioStreamIndexes{};   // Index all the audio streams of the file
     QMap<uint, StreamPtr> audioStreams{}; // THe audio streams of the file
 
     int defaultStreamIndex{ -1 };
diff --git a/src/Lib/Document/AbstractDocument.hh b/src/Lib/Document/AbstractDocument.hh
index e6295263f040572bc680b5c3a97f6244da07e11d..823a1a83a152f34ce73e4b96ceacdaccd9dc9b8c 100644
--- a/src/Lib/Document/AbstractDocument.hh
+++ b/src/Lib/Document/AbstractDocument.hh
@@ -5,9 +5,14 @@
 #error "This is a C++ header"
 #endif
 
+#include "../Utils.hh"
+
 namespace Vivy
 {
-class AbstractDocument {
+class AbstractDocument : public QObject {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(AbstractDocument)
+
 protected:
     AbstractDocument()                   = default;
     virtual ~AbstractDocument() noexcept = default;
@@ -15,6 +20,9 @@ protected:
 public:
     virtual bool rename(const QString &) noexcept = 0;
     virtual QString getName() const noexcept      = 0;
+
+signals:
+    void documentChanged();
 };
 }
 
diff --git a/src/Lib/Document/CRTPSubDocument.cc b/src/Lib/Document/CRTPSubDocument.cc
index d6ac414b607200268e9d6a2b7a9755fd6d59a6ce..ed788183a5f1acc939f1c28479d9cfeec9951e42 100644
--- a/src/Lib/Document/CRTPSubDocument.cc
+++ b/src/Lib/Document/CRTPSubDocument.cc
@@ -25,17 +25,11 @@ AudioSubDocument::getDefaultStream() const noexcept
     if (auto ptr = contextPtr->getDefaultStream().lock()) {
         return ptr;
     } else {
+        qCritical() << "Document deleted!";
         return nullptr;
     }
 }
 
-// Get the stream count, may be 0
-int
-AudioSubDocument::getStreamCount() const noexcept
-{
-    return contextPtr->getStreamCount();
-}
-
 // Get the stream asked for, nullptr if no stream or if the index is invalid
 AudioContext::StreamPtr
 AudioSubDocument::getStream(int index) const noexcept
@@ -68,9 +62,10 @@ QJsonDocument
 AudioSubDocument::getProperties() const noexcept
 {
     QJsonDocument ret;
-    QJsonObject object;
-    const QJsonDocument contextDocument = contextPtr->getProperties();
-    object.insert("Audio context", contextDocument.object());
+    QJsonObject object{
+        { "Audio context", contextPtr->getProperties().object() },
+        { "File", filePath },
+    };
     ret.setObject(object);
     return ret;
 }
@@ -108,17 +103,39 @@ AssSubDocument::initFromPath(const QString &path)
     factory.getStyles(styles);
     factory.getLines(lines);
 }
+
 QString
 AssSubDocument::getElementName() const noexcept
 {
     return "AssSubDocument";
 }
 
+const QVector<Ass::LinePtr> &
+AssSubDocument::getLines() const noexcept
+{
+    return lines;
+}
+
+const QVector<Ass::StylePtr> &
+AssSubDocument::getStyles() const noexcept
+{
+    return styles;
+}
+
 QJsonDocument
 AssSubDocument::getProperties() const noexcept
 {
     QJsonDocument ret;
-    QJsonObject object;
+
+    QJsonObject styleObject;
+    for (const Ass::StylePtr &style : styles) {
+        styleObject.insert(style->getElementName(), style->getProperties().object());
+    }
+
+    QJsonObject object{
+        { "Styles", styleObject },
+        { "File", filePath },
+    };
     ret.setObject(object);
     return ret;
 }
diff --git a/src/Lib/Document/CRTPSubDocument.hh b/src/Lib/Document/CRTPSubDocument.hh
index f89b26f65e235f3c3b3fd72a22510c7afd7cebdd..715ec6bfbdd765cc8d7bffc1aaaf12dc083950c9 100644
--- a/src/Lib/Document/CRTPSubDocument.hh
+++ b/src/Lib/Document/CRTPSubDocument.hh
@@ -41,7 +41,7 @@ public:
         try {
             ret->initFromPath(path); // May throw
         } catch (const std::runtime_error &e) {
-            qDebug() << "Failed to init document from file" << path;
+            qDebug().nospace() << "Failed to init document from file " << path << ": " << e.what();
             ret.reset();
         }
 
@@ -75,7 +75,6 @@ private:
 public:
     int getDefaultStreamIndex() const noexcept;
     AudioContext::StreamPtr getDefaultStream() const noexcept;
-    int getStreamCount() const noexcept;
     AudioContext::StreamPtr getStream(int index) const noexcept;
 
     QString getElementName() const noexcept;
@@ -110,6 +109,9 @@ public:
     QString getElementName() const noexcept;
     QJsonDocument getProperties() const noexcept;
 
+    const QVector<Ass::LinePtr> &getLines() const noexcept;
+    const QVector<Ass::StylePtr> &getStyles() const noexcept;
+
 private:
     QVector<Ass::StylePtr> styles;
     QVector<Ass::LinePtr> lines;
diff --git a/src/Lib/Document/VivyDocument.cc b/src/Lib/Document/VivyDocument.cc
index cc2f3aaeffc5847e4dd995be1dcb8233665a4964..ed8fbfc66a7cfeb545919b2f8ded803a823135d6 100644
--- a/src/Lib/Document/VivyDocument.cc
+++ b/src/Lib/Document/VivyDocument.cc
@@ -199,10 +199,8 @@ VivyDocument::setAudioSubDocument(const QString filename) noexcept
     }
 
     audioDocument = AudioSubDocument::fromFile(filename);
-    if (audioDocument) {
-        documentType |= Capabilities::AudioAble;
-        documentOptions &= ~Options::UntouchedByDefault;
-    }
+    if (audioDocument)
+        addDocumentType(AudioAble);
 }
 
 void
@@ -218,10 +216,8 @@ VivyDocument::setVideoSubDocument(const QString filename) noexcept
     }
 
     videoDocument = VideoSubDocument::fromFile(filename);
-    if (videoDocument) {
-        documentType |= Capabilities::VideoAble;
-        documentOptions &= ~Options::UntouchedByDefault;
-    }
+    if (videoDocument)
+        addDocumentType(VideoAble);
 }
 
 void
@@ -237,10 +233,16 @@ VivyDocument::setAssSubDocument(const QString filename) noexcept
     }
 
     assDocument = AssSubDocument::fromFile(filename);
-    if (assDocument) {
-        documentType |= Capabilities::AssAble;
-        documentOptions &= ~Options::UntouchedByDefault;
-    }
+    if (assDocument)
+        addDocumentType(AssAble);
+}
+
+void
+VivyDocument::addDocumentType(Capabilities newCap) noexcept
+{
+    documentType |= newCap;
+    documentOptions &= ~Options::UntouchedByDefault;
+    emit documentChanged();
 }
 
 QString
diff --git a/src/Lib/Document/VivyDocument.hh b/src/Lib/Document/VivyDocument.hh
index 10a8aeb2a11ee319de000f4c20a0541a2e406405..9ecfbaa56c186cd73c677fb5ed0eb2e075a77495 100644
--- a/src/Lib/Document/VivyDocument.hh
+++ b/src/Lib/Document/VivyDocument.hh
@@ -18,6 +18,9 @@
 namespace Vivy
 {
 class VivyDocument final : public AbstractDocument {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(VivyDocument)
+
 public:
     enum Capabilities : quint64 {
         AudioAble = (1 << 1),
@@ -63,6 +66,8 @@ private:
 
     static bool detectDocumentType(const QFileInfo &file, Capabilities *) noexcept;
 
+    void addDocumentType(Capabilities) noexcept;
+
 public:
     // Create an empty document
     explicit VivyDocument(const QString &name, Options opt = NoOption);
diff --git a/src/Lib/Utils.cc b/src/Lib/Utils.cc
index b49fa9ee2ee3e2ce33226cba8797f5d028ca754a..2e1a1b5fa800e146cee8033a432fd7a68ec65a51 100644
--- a/src/Lib/Utils.cc
+++ b/src/Lib/Utils.cc
@@ -1,5 +1,6 @@
 #include "Utils.hh"
 
+#include <QRegExp>
 #include <QFileInfo>
 
 using namespace Vivy;
@@ -24,3 +25,60 @@ Utils::detectDocumentType(const QFileInfo &file, DocumentType *type)
 
     return false;
 }
+
+bool
+Utils::decodeLineToBoolean(const QString &item, const QString &error)
+{
+    return Utils::decodeLineToInteger(item, error) >= 1;
+}
+
+int
+Utils::decodeLineToInteger(const QString &item, const QString &error)
+{
+    bool conversion = false;
+    int ret         = item.toInt(&conversion);
+    if (!conversion)
+        throw std::runtime_error(("invalid line content: " + error).toStdString());
+    return ret;
+}
+
+float
+Utils::decodeLineToFloating(const QString &item, const QString &error)
+{
+    bool conversion = false;
+    float ret       = item.toFloat(&conversion);
+    if (!conversion)
+        throw std::runtime_error(("invalid line content: " + error).toStdString());
+    return ret;
+}
+
+// The string must be of the form: `H:MM:SS.cs`
+Utils::Time
+Utils::Time::fromString(const QString &str)
+{
+    QRegExp re("^(\\d+):(\\d\\d):(\\d\\d)\\.(\\d\\d)$");
+
+    // Here the toUint is safe because the RegExp is OK
+    if (re.indexIn(str) != -1)
+        return { .hour        = re.cap(1).toUInt(),
+                 .minute      = re.cap(2).toUInt(),
+                 .second      = re.cap(3).toUInt(),
+                 .centisecond = re.cap(4).toUInt() };
+
+    else
+        throw std::runtime_error("The string is not of the format `H:MM:SS.cs`");
+}
+
+quint64
+Utils::Time::toUInt() const noexcept
+{
+    return ((hour * 3600) + (minute * 60) + second) * 100 + centisecond;
+}
+
+// Get a string from a time
+QString
+Utils::Time::toString() const noexcept
+{
+    return QString::number(hour) + ":" + QString::number(minute) + ":" + QString::number(second) +
+           "." + QString::number(centisecond);
+}
diff --git a/src/Lib/Utils.hh b/src/Lib/Utils.hh
index eeba9f84c41a63e6bf3eeb23d2db3b4d602f1a4d..ef38724c7c54862d236bb0fb7db4c36389f00483 100644
--- a/src/Lib/Utils.hh
+++ b/src/Lib/Utils.hh
@@ -84,7 +84,23 @@ toUnderlying(E e) noexcept
     return static_cast<std::underlying_type_t<E>>(e);
 }
 
+// Structure used to represent the time as it is presented in an ASS file.
+struct Time final {
+    quint64 hour;
+    quint64 minute;
+    quint64 second;
+    quint64 centisecond;
+
+    static Time fromString(const QString &);
+
+    QString toString() const noexcept;
+    quint64 toUInt() const noexcept;
+};
+
 bool detectDocumentType(const QFileInfo &, DocumentType *);
+bool decodeLineToBoolean(const QString &item, const QString &error);
+int decodeLineToInteger(const QString &item, const QString &error);
+float decodeLineToFloating(const QString &item, const QString &error);
 }
 
 namespace Vivy
diff --git a/src/UI/AbstractDocumentView.hh b/src/UI/AbstractDocumentView.hh
index 0ce9a6e03a4957ca3e9f8b5e3018bff4f3af9b87..e73814ba43fd51bd424095a6378e1d57265dd027 100644
--- a/src/UI/AbstractDocumentView.hh
+++ b/src/UI/AbstractDocumentView.hh
@@ -1,5 +1,4 @@
-#ifndef VIVY_DOCUMENT_VIEW_H
-#define VIVY_DOCUMENT_VIEW_H
+#pragma once
 
 #ifndef __cplusplus
 #error "This is a C++ header"
@@ -56,5 +55,3 @@ private:
     const Type documentType;
 };
 }
-
-#endif // VIVY_DOCUMENT_VIEW_H
diff --git a/src/UI/DocumentViews/AssLinesModel.cc b/src/UI/DocumentViews/AssLinesModel.cc
new file mode 100644
index 0000000000000000000000000000000000000000..99cbc42e322449684b824a3c7bedafeb09c92bc1
--- /dev/null
+++ b/src/UI/DocumentViews/AssLinesModel.cc
@@ -0,0 +1,155 @@
+#include "AssLinesModel.hh"
+
+using namespace Vivy;
+
+AssLinesModel::Item::Item(Ass::LineWeakPtr linePtr) noexcept
+    : line(linePtr)
+{
+}
+
+quint64
+AssLinesModel::Item::getLineNumber() const noexcept
+{
+    return 0;
+}
+
+QString
+AssLinesModel::Item::getLineText() const noexcept
+{
+    QString ret;
+    if (auto ptr = line.lock()) {
+        for (const auto &syl : ptr->getContent()) {
+            ret.append(syl.getContent());
+            ret.append("|");
+        }
+    }
+    ret.remove(ret.size() - 1);
+    return ret;
+}
+
+bool
+AssLinesModel::Item::getIsComment() const noexcept
+{
+    if (auto ptr = line.lock())
+        return ptr->getIsComment();
+    return false;
+}
+
+QString
+AssLinesModel::Item::getLineStyle() const noexcept
+{
+    if (auto ptr = line.lock()) {
+        if (auto style = ptr->getStyle().lock()) {
+            return style->getElementName();
+        }
+    }
+    return "";
+}
+
+AssLinesModel::AssLinesModel(const QVector<Ass::LinePtr> &lines) noexcept
+    : lineRealData(lines)
+{
+    childs.reserve(lines.count());
+    for (const Ass::LinePtr &line : lines)
+        childs.append(new Item(Ass::LineWeakPtr{ line }));
+}
+
+AssLinesModel::~AssLinesModel() noexcept
+{
+    qDeleteAll(childs);
+}
+
+QVariant
+AssLinesModel::data(const QModelIndex &index, int role) const noexcept
+{
+    if (!index.isValid() || index.column() >= Utils::toUnderlying(Item::Field::TotalFieldCount))
+        return QVariant();
+
+    const Item *line         = static_cast<const Item *>(index.internalPointer());
+    const Item::Field column = static_cast<Item::Field>(index.column());
+
+    // Display role
+    if (Qt::DisplayRole == role) {
+        switch (column) {
+        case Item::Field::Text:
+            return line->getLineText();
+
+        // We want a warning when adding a field so don"t use "default"
+        case Item::Field::TotalFieldCount:
+            qFatal("Unreachable");
+        }
+    }
+
+    // Happily ignore the edit role for now
+    else if (Qt::EditRole == role) {
+    }
+
+    return QVariant();
+}
+
+bool
+AssLinesModel::setData(const QModelIndex &index, const QVariant & /* value */, int role) noexcept
+{
+    const int col = index.column();
+    if (col >= Utils::toUnderlying(Item::Field::TotalFieldCount))
+        return false;
+
+    if (Qt::EditRole == role) {
+        emit dataChanged(index, index, { Qt::EditRole });
+        return true;
+    }
+
+    return false;
+}
+
+QVariant
+AssLinesModel::headerData(int section, Qt::Orientation oriantation, int role) const noexcept
+{
+    if (Qt::DisplayRole != role)
+        return QVariant();
+
+    else if (Qt::Horizontal == oriantation)
+        return headers.value(section);
+
+    else if (Qt::Vertical == oriantation)
+        return section + 1;
+
+    else
+        return QVariant();
+}
+
+QModelIndex
+AssLinesModel::parent(const QModelIndex &) const noexcept
+{
+    return QModelIndex();
+}
+
+QModelIndex
+AssLinesModel::index(int row, int column, const QModelIndex &parent) const noexcept
+{
+    if (!hasIndex(row, column, parent))
+        return QModelIndex();
+    else if (row >= childs.size())
+        return QModelIndex();
+    else
+        return createIndex(row, column, childs[row]);
+}
+
+int
+AssLinesModel::rowCount(const QModelIndex & /* parent */) const noexcept
+{
+    return childs.size();
+}
+
+int
+AssLinesModel::columnCount(const QModelIndex & /* parent */) const noexcept
+{
+    return Utils::toUnderlying(Item::Field::TotalFieldCount);
+}
+
+Qt::ItemFlags
+AssLinesModel::flags(const QModelIndex &index) const noexcept
+{
+    [[maybe_unused]] const Item *item = static_cast<const Item *>(index.internalPointer());
+    return Qt::ItemNeverHasChildren | Qt::ItemIsSelectable | QAbstractItemModel::flags(index);
+}
diff --git a/src/UI/DocumentViews/AssLinesModel.hh b/src/UI/DocumentViews/AssLinesModel.hh
new file mode 100644
index 0000000000000000000000000000000000000000..2dfdbcd1c709a55ce9c02e902f628358109dc2ed
--- /dev/null
+++ b/src/UI/DocumentViews/AssLinesModel.hh
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "../../Lib/Utils.hh"
+#include "../../Lib/Ass/Ass.hh"
+#include <QAbstractItemModel>
+#include <QStringList>
+
+namespace Vivy
+{
+class AssLinesModel final : public QAbstractItemModel {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(AssLinesModel)
+
+private:
+    class Item final {
+        VIVY_UNMOVABLE_OBJECT(Item)
+
+    public:
+        Item(Ass::LineWeakPtr linePtr) noexcept;
+        ~Item() noexcept = default;
+
+        enum class Field : int {
+            Text,
+
+            // Last, the count
+            TotalFieldCount,
+        };
+
+        bool getIsComment() const noexcept;
+        quint64 getLineNumber() const noexcept;
+        QString getLineText() const noexcept;
+        QString getLineStyle() const noexcept;
+
+    private:
+        Ass::LineWeakPtr line;
+    };
+
+    static inline const QStringList headers{ "", "Style", "Text" };
+
+public:
+    explicit AssLinesModel(const QVector<Ass::LinePtr> &) noexcept;
+    ~AssLinesModel() noexcept;
+
+    QVariant data(const QModelIndex &, int role) const noexcept override;
+    bool setData(const QModelIndex &, const QVariant &v, int r = Qt::EditRole) noexcept override;
+
+    QVariant headerData(int section, Qt::Orientation, int r) const noexcept override;
+    QModelIndex parent(const QModelIndex &) const noexcept override;
+    QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const noexcept override;
+
+    int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept override;
+    int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept override;
+
+    Qt::ItemFlags flags(const QModelIndex &) const noexcept override;
+
+private:
+    QVector<Item *> childs;
+    const QVector<Ass::LinePtr> &lineRealData;
+};
+}
diff --git a/src/UI/DocumentViews/AssLinesView.cc b/src/UI/DocumentViews/AssLinesView.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a29245be8ae0b58d1a78a0fb285f73df24bd0751
--- /dev/null
+++ b/src/UI/DocumentViews/AssLinesView.cc
@@ -0,0 +1,126 @@
+#include "AssLinesView.hh"
+#include "../../VivyApplication.hh"
+#include <QPaintEvent>
+#include <QHeaderView>
+
+using namespace Vivy;
+
+AssLinesView::AssLinesView(QAbstractItemModel *model, QWidget *parent) noexcept
+    : QTableView(parent)
+    , delegate(new Delegate(this))
+{
+    // Force the 9pt font size for now
+    {
+        QFont f = font();
+        f.setPointSize(9);
+        setFont(f);
+    }
+
+    setMouseTracking(true);
+    setItemDelegate(delegate);
+    connect(this, &AssLinesView::hoverIndexChanged, delegate,
+            &AssLinesView::Delegate::onItemEntered);
+
+    horizontalHeader()->setHighlightSections(false);
+    horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+    horizontalHeader()->setStretchLastSection(true);
+    horizontalHeader()->hide();
+    verticalHeader()->setVisible(true);
+    setSelectionMode(QAbstractItemView::ExtendedSelection);
+    setSelectionBehavior(QAbstractItemView::SelectRows);
+    setShowGrid(false);
+
+    // Also set the 9pt font size for the headers
+    {
+        QFont f = horizontalHeader()->font();
+        f.setPointSize(9);
+        horizontalHeader()->setFont(f);
+        verticalHeader()->setFont(f);
+    }
+
+    static const QString style = "QTableView::item:selected {"
+                                 "  border-top:1px solid #1394B4;"
+                                 "  border-bottom:1px solid #1394B4;"
+                                 "  border-right:1px solid #1394B4;"
+                                 "  border-left:1px solid #1394B4;"
+                                 "  background-color: #002B36;"
+                                 "}"
+                                 "QTableView::item {"
+                                 "  border: 1px solid #474747;"
+                                 "};"
+                                 "QTableView::item:last:selected {"
+                                 "  border-top:1px solid #1394B4;"
+                                 "  border-bottom:1px solid #1394B4;"
+                                 "  border-right:1px solid #1394B4;"
+                                 "  border-left:1px solid #1394B4;"
+                                 "  background-color: #002B36;"
+                                 "}";
+    setStyleSheet(style);
+    setModel(model);
+}
+
+void
+AssLinesView::paintEvent(QPaintEvent *e) noexcept
+{
+    QTableView::paintEvent(e);
+}
+
+void
+AssLinesView::mouseMoveEvent(QMouseEvent *event) noexcept
+{
+    QModelIndex index = indexAt(event->pos());
+    emit hoverIndexChanged(index);
+}
+
+AssLinesView::Delegate::Delegate(AssLinesView *view, QWidget *parent) noexcept
+    : QStyledItemDelegate(parent)
+    , tableView(view)
+{
+}
+
+bool
+AssLinesView::Delegate::isHoveredRow(const QStyleOptionViewItem &option,
+                                     const QModelIndex &index) const noexcept
+{
+    bool ret            = false;
+    const int gridWidth = tableView->showGrid() ? 1 : 0;
+
+    for (int i = 0; i < index.model()->columnCount(); ++i) {
+        QModelIndex idx = index.model()->index(index.row(), i);
+        if (const QAbstractItemView *view =
+                qobject_cast<const QAbstractItemView *>(option.widget)) {
+            QStyleOptionViewItem o;
+            o.initFrom(view);
+            o.rect = view->visualRect(idx).adjusted(0, 0, gridWidth, gridWidth);
+            if (o.rect.contains(view->viewport()->mapFromGlobal(QCursor::pos()))) {
+                ret = true;
+            }
+        }
+    }
+
+    return ret;
+}
+
+void
+AssLinesView::Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
+                              const QModelIndex &index) const noexcept
+{
+    QStyleOptionViewItem opt = option;
+    if (index.row() == hoveredRow) {
+        opt.state |= QStyle::State_MouseOver;
+    }
+    QStyledItemDelegate::paint(painter, opt, index);
+}
+
+void
+AssLinesView::Delegate::onItemEntered(const QModelIndex &index) noexcept
+{
+    hoveredRow = index.row();
+}
+
+void
+AssLinesView::Delegate::initStyleOption(QStyleOptionViewItem *option,
+                                        const QModelIndex &index) const noexcept
+{
+    QStyledItemDelegate::initStyleOption(option, index);
+}
diff --git a/src/UI/DocumentViews/AssLinesView.hh b/src/UI/DocumentViews/AssLinesView.hh
new file mode 100644
index 0000000000000000000000000000000000000000..54c58c660067f55270af7fa92d3ede42074823ae
--- /dev/null
+++ b/src/UI/DocumentViews/AssLinesView.hh
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "../../Lib/Utils.hh"
+#include <QAbstractItemModel>
+#include <QTableView>
+#include <QStyledItemDelegate>
+
+namespace Vivy
+{
+class AssLinesView final : public QTableView {
+    Q_OBJECT
+    VIVY_UNMOVABLE_OBJECT(AssLinesView)
+
+private:
+    class Delegate : public QStyledItemDelegate {
+        VIVY_UNMOVABLE_OBJECT(Delegate)
+
+    private:
+        bool isHoveredRow(const QStyleOptionViewItem &, const QModelIndex &) const noexcept;
+
+        int hoveredRow{ 0 };
+        AssLinesView *tableView{ nullptr };
+
+    public:
+        Delegate(AssLinesView *, QWidget *parent = nullptr) noexcept;
+        ~Delegate() noexcept = default;
+
+        void initStyleOption(QStyleOptionViewItem *option,
+                             const QModelIndex &index) const noexcept override;
+
+        void paint(QPainter *, const QStyleOptionViewItem &,
+                   const QModelIndex &) const noexcept override;
+        void onItemEntered(const QModelIndex &) noexcept;
+    };
+
+private:
+    Delegate *delegate{ nullptr };
+
+public:
+    AssLinesView(QAbstractItemModel *, QWidget *parent = nullptr) noexcept;
+    ~AssLinesView() noexcept = default;
+
+    void paintEvent(QPaintEvent *) noexcept override;
+
+private:
+    void mouseMoveEvent(QMouseEvent *) noexcept override;
+
+signals:
+    void hoverIndexChanged(QModelIndex);
+};
+}
diff --git a/src/UI/PropertyModel.cc b/src/UI/PropertyModel.cc
index b32144f0ed5a76a8dce19077f348a5527ad1ed59..47308fa476f236532eaa12078efce9ae338754ea 100644
--- a/src/UI/PropertyModel.cc
+++ b/src/UI/PropertyModel.cc
@@ -79,7 +79,7 @@ PropertyModel::Item::getType() const noexcept
     return type;
 }
 
-const QList<PropertyModel::Item *>
+const QVector<PropertyModel::Item *>
 PropertyModel::Item::getChilds() const noexcept
 {
     return childs;
diff --git a/src/UI/PropertyModel.hh b/src/UI/PropertyModel.hh
index c6cdca5eb16601a03c1a4d46c01c4ae8dba2d454..c79f0aa831199e4b8726d6adcf37d6c76c734992 100644
--- a/src/UI/PropertyModel.hh
+++ b/src/UI/PropertyModel.hh
@@ -37,7 +37,7 @@ private:
 
         void appendChild(Item *) noexcept;
         Item *getChild(int row) const noexcept;
-        const QList<Item *> getChilds() const noexcept;
+        const QVector<Item *> getChilds() const noexcept;
         Item *getParent() const noexcept;
         int getChildCount() const noexcept;
         int getRow() const noexcept;
@@ -55,7 +55,7 @@ private:
         QString key{};
         QString value{};
         QJsonValue::Type type{ QJsonValue::Null };
-        QList<Item *> childs{};
+        QVector<Item *> childs{};
         Item *parent{ nullptr };
     };
 
@@ -77,8 +77,7 @@ public:
     void setEditable(const bool);
 
     QVariant data(const QModelIndex &, int role) const noexcept override;
-    bool setData(const QModelIndex &, const QVariant &value,
-                 int role = Qt::EditRole) noexcept override;
+    bool setData(const QModelIndex &, const QVariant &v, int r = Qt::EditRole) noexcept override;
 
     QVariant headerData(int section, Qt::Orientation, int role) const noexcept override;
     QModelIndex parent(const QModelIndex &) const noexcept override;
diff --git a/src/UI/VivyDocumentView.cc b/src/UI/VivyDocumentView.cc
index 91362576fa42d62df995bace7ca902f11423f2bc..d8af3d93b9cb0e32374f1f788c95411748153106 100644
--- a/src/UI/VivyDocumentView.cc
+++ b/src/UI/VivyDocumentView.cc
@@ -1,12 +1,14 @@
 #include "VivyDocumentView.hh"
 #include "PropertyModel.hh"
 #include "DocumentViews/AudioVisualizer.hh"
+#include "DocumentViews/AssLinesView.hh"
 #include "../VivyApplication.hh"
 #include "../Lib/Document/VivyDocument.hh"
 
 #include <QHeaderView>
 #include <QTreeView>
 #include <QVBoxLayout>
+#include <QTableView>
 
 using namespace Vivy;
 
@@ -24,9 +26,13 @@ VivyDocumentView::VivyDocumentView(std::shared_ptr<VivyDocument> doc, QWidget *p
     setDockNestingEnabled(true);
 
     // Add some actions...
-    QAction *openProperties = new QAction("Open properties", this);
-    connect(openProperties, &QAction::triggered, this, &VivyDocumentView::openProperties);
-    viewsActions.append(openProperties);
+    QAction *openPropertiesAct = new QAction("Open properties", this);
+    connect(openPropertiesAct, &QAction::triggered, this, &VivyDocumentView::openProperties);
+    connect(document.get(), &AbstractDocument::documentChanged, this, [=, this]() noexcept -> void {
+        if (property)
+            openProperties();
+    });
+    viewsActions.append(openPropertiesAct);
 
     // The separator
     QAction *separator = new QAction(this);
@@ -72,6 +78,18 @@ VivyDocumentView::loadVideoView() noexcept
 void
 VivyDocumentView::loadAssView() noexcept
 {
+    if (assLines)
+        delDockWidget(&assLines);
+
+    if (document->checkDocumentCapabilities(VivyDocument::Capabilities::AssAble)) {
+        assModel.reset(new AssLinesModel(document->getAssSubDocument()->getLines()));
+        assLines = new QDockWidget("ASS Lines", this);
+        assLines->setWidget(new AssLinesView(assModel.get(), assLines));
+        assLines->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable);
+        assLines->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea |
+                                  Qt::BottomDockWidgetArea);
+        addDockWidget(Qt::BottomDockWidgetArea, assLines, Qt::Vertical);
+    }
 }
 
 void
diff --git a/src/UI/VivyDocumentView.hh b/src/UI/VivyDocumentView.hh
index 493b75e1112f8d4e2e259ef3f173448a5b4aab5b..b30b27d14dcbc2290f6e0b4c082ab029607cede1 100644
--- a/src/UI/VivyDocumentView.hh
+++ b/src/UI/VivyDocumentView.hh
@@ -7,6 +7,7 @@
 
 #include "../Lib/Document/VivyDocument.hh"
 #include "DocumentViews/AudioVisualizer.hh"
+#include "DocumentViews/AssLinesModel.hh"
 #include "AbstractDocumentView.hh"
 #include "PropertyModel.hh"
 
@@ -39,9 +40,11 @@ public slots:
 
 private:
     std::shared_ptr<VivyDocument> document;
-    std::unique_ptr<PropertyModel> propertyModel;
+    std::unique_ptr<PropertyModel> propertyModel{ nullptr };
+    std::unique_ptr<AssLinesModel> assModel{ nullptr };
     QDockWidget *visualizer{ nullptr };
     QDockWidget *property{ nullptr };
+    QDockWidget *assLines{ nullptr };
 };
 
 }
diff --git a/src/VivyApplication.cc b/src/VivyApplication.cc
index 5b23121370541f8427260fd27a3439c45f65a1bd..0b08c8017def22582ac61c7f35115599721d9835 100644
--- a/src/VivyApplication.cc
+++ b/src/VivyApplication.cc
@@ -11,7 +11,10 @@ VivyApplication::VivyApplication(int argc, char **argv)
 int
 VivyApplication::exec() noexcept
 {
+    // Show the main window
     MainWindow mainWindow;
     mainWindow.show();
+
+    // Main loop
     return QApplication::exec();
 }