carolisteia
Clean mobile CSS and keep display warning
50d9b81
Raw
History Blame Contribute Delete
62.8 kB
import os
os.environ["GRADIO_SSR_MODE"] = "False"
import html
import urllib.parse
from pathlib import Path
import gradio as gr
import pandas as pd
DATASETS = {
"Lancelot": {
"Reviewed": Path("sample_data/reviewed_lancelot.csv"),
"Raw": Path("sample_data/raw_lancelot.csv"),
},
"De regimine principum": {
"Reviewed": Path("sample_data/reviewed_de_regimine.csv"),
"Raw": Path("sample_data/raw_de_regimine.csv"),
},
}
def get_data_file(corpus_name, alignment_type):
return DATASETS[corpus_name][alignment_type]
def load_alignments(corpus_name, alignment_type):
data_file = get_data_file(corpus_name, alignment_type)
if not data_file.exists():
return pd.DataFrame({"segment_id": []})
df = pd.read_csv(data_file, sep=None, engine="python", dtype=str).fillna("")
df = df.rename(columns={df.columns[0]: "segment_id"})
return df
def get_segment_choices(corpus_name, alignment_type):
df = load_alignments(corpus_name, alignment_type)
if df.empty:
return []
return df["segment_id"].astype(str).tolist()
def search_segments(corpus_name, alignment_type, query):
df = load_alignments(corpus_name, alignment_type)
if df.empty:
return []
if query is None or not str(query).strip():
return df["segment_id"].astype(str).tolist()
query = str(query).strip().lower()
matching_segments = []
for _, row in df.iterrows():
row_text = " ".join(str(value).lower() for value in row.values)
if query in row_text:
matching_segments.append(str(row["segment_id"]))
return matching_segments
def get_witness_choices(corpus_name, alignment_type):
df = load_alignments(corpus_name, alignment_type)
if df.empty:
return []
witness_columns = [col for col in df.columns if col != "segment_id"]
if len(witness_columns) <= 1:
return []
return witness_columns[1:]
def clean_witness_name(name):
return str(name).replace("-", " ").upper()
def view_segment(corpus_name, alignment_type, segment_id, selected_witnesses=None):
df = load_alignments(corpus_name, alignment_type)
if df.empty:
return "<p>No data found for this corpus and alignment type.</p>"
row_match = df[df["segment_id"].astype(str) == str(segment_id)]
if row_match.empty:
return "<p>No segment selected.</p>"
row = row_match.iloc[0]
witness_columns = [col for col in df.columns if col != "segment_id"]
if not witness_columns:
return "<p>No witness columns found.</p>"
main_witness = witness_columns[0]
other_witnesses = witness_columns[1:]
if selected_witnesses:
other_witnesses = [w for w in other_witnesses if w in selected_witnesses]
main_text = str(row[main_witness]).strip()
warning = ""
if alignment_type == "Raw":
warning = """
<div class="raw-warning">
Raw Aquilign output is shown for transparency. It may contain alignment errors
and should not be considered manually validated scholarly alignment.
</div>
"""
html_output = f"""
<div class="reading-view">
{warning}
<div class="segment-heading">
Aligned segment <span>{html.escape(str(segment_id))}</span>
</div>
<div class="main-witness-card">
<div class="witness-label">Main witness</div>
<div class="witness-meta">{html.escape(clean_witness_name(main_witness))}</div>
<div class="main-witness-text">{html.escape(main_text)}</div>
</div>
<div class="witness-section-title">Witnesses</div>
<div class="parallel-view">
"""
for witness in other_witnesses:
text = str(row[witness]).strip()
if not text:
continue
html_output += f"""
<div class="witness-card">
<div class="witness-meta">{html.escape(clean_witness_name(witness))}</div>
<div class="witness-text">{html.escape(text)}</div>
</div>
"""
html_output += """
</div>
</div>
"""
return html_output
def corpus_notice(corpus_name, alignment_type):
df = load_alignments(corpus_name, alignment_type)
if df.empty:
return """
<div class="corpus-notice">
No data found for this corpus.
</div>
"""
witness_columns = [col for col in df.columns if col != "segment_id"]
segment_count = len(df)
witness_count = len(witness_columns)
return f"""
<div class="corpus-notice">
<strong>{html.escape(corpus_name)}</strong> · {html.escape(alignment_type)} alignments<br>
{segment_count} aligned segments · {witness_count} witnesses / textual versions
</div>
"""
def get_download_file(corpus_name, alignment_type):
data_file = get_data_file(corpus_name, alignment_type)
if data_file.exists():
return gr.update(value=str(data_file))
return gr.update(value=None)
def make_issue_report(corpus_name, alignment_type, segment_id):
if not segment_id:
return ""
return f"""Corpus: {corpus_name}
Alignment type: {alignment_type}
Segment: {segment_id}
Describe the alignment issue here:
"""
def make_github_issue_link(corpus_name, alignment_type, segment_id):
if not segment_id:
return ""
title = f"Alignment issue: {corpus_name}, segment {segment_id}"
body = make_issue_report(corpus_name, alignment_type, segment_id)
url = (
"https://github.com/ProMeText/Aquilign/issues/new?"
+ urllib.parse.urlencode({"title": title, "body": body})
)
return f"""
<div class="report-box">
<a href="{url}" target="_blank">Open a GitHub issue for this segment</a>
</div>
"""
def update_explore(corpus_name, alignment_type):
df = load_alignments(corpus_name, alignment_type)
segments = get_segment_choices(corpus_name, alignment_type)
witnesses = get_witness_choices(corpus_name, alignment_type)
if not segments:
return (
gr.update(choices=[], value=None),
"",
gr.update(choices=[], value=[]),
"<p>No data found.</p>",
df,
corpus_notice(corpus_name, alignment_type),
get_download_file(corpus_name, alignment_type),
"",
"",
)
first_segment = segments[0]
return (
gr.update(choices=segments, value=first_segment),
"",
gr.update(choices=witnesses, value=witnesses),
view_segment(corpus_name, alignment_type, first_segment, witnesses),
df,
corpus_notice(corpus_name, alignment_type),
get_download_file(corpus_name, alignment_type),
make_issue_report(corpus_name, alignment_type, first_segment),
make_github_issue_link(corpus_name, alignment_type, first_segment),
)
def update_search(corpus_name, alignment_type, query, selected_witnesses):
segments = search_segments(corpus_name, alignment_type, query)
if not segments:
return (
gr.update(choices=[], value=None),
"<p>No matching segment found.</p>",
'<div class="search-status">No matching segment found. Clear the search to return to the full corpus.</div>',
"",
"",
)
first_segment = segments[0]
search_status = ""
if query and str(query).strip():
search_status = f"""
<div class="search-status">
Search results for <strong>{html.escape(str(query))}</strong>: {len(segments)} matching segment(s).
Use the segment menu or Previous / Next to browse them.
</div>
"""
return (
gr.update(choices=segments, value=first_segment),
view_segment(corpus_name, alignment_type, first_segment, selected_witnesses),
search_status,
make_issue_report(corpus_name, alignment_type, first_segment),
make_github_issue_link(corpus_name, alignment_type, first_segment),
)
def clear_search(corpus_name, alignment_type, selected_witnesses):
segments = get_segment_choices(corpus_name, alignment_type)
if not segments:
return (
gr.update(value=""),
gr.update(choices=[], value=None),
"<p>No data found.</p>",
"",
"",
"",
)
first_segment = segments[0]
return (
gr.update(value=""),
gr.update(choices=segments, value=first_segment),
view_segment(corpus_name, alignment_type, first_segment, selected_witnesses),
"",
make_issue_report(corpus_name, alignment_type, first_segment),
make_github_issue_link(corpus_name, alignment_type, first_segment),
)
def move_segment(corpus_name, alignment_type, current_segment, query, selected_witnesses, direction):
segments = search_segments(corpus_name, alignment_type, query)
if not segments:
return (
gr.update(choices=[], value=None),
"<p>No segment available.</p>",
"",
"",
)
current_segment = str(current_segment)
if current_segment in segments:
current_index = segments.index(current_segment)
else:
current_index = 0
if direction == "previous":
new_index = max(0, current_index - 1)
else:
new_index = min(len(segments) - 1, current_index + 1)
new_segment = segments[new_index]
return (
gr.update(choices=segments, value=new_segment),
view_segment(corpus_name, alignment_type, new_segment, selected_witnesses),
make_issue_report(corpus_name, alignment_type, new_segment),
make_github_issue_link(corpus_name, alignment_type, new_segment),
)
custom_css = """
html,
body,
#root,
.gradio-container,
.app,
.main,
.wrap,
.contain,
footer {
background-color: #f7f3ec !important;
}
.gradio-container {
max-width: 1200px !important;
margin: auto !important;
background: #f7f3ec !important;
color: #2b241f !important;
}
#main-banner {
width: 100% !important;
max-width: 100% !important;
height: 370px !important;
margin-top: -20px !important;
margin-bottom: -20px !important;
overflow: hidden !important;
}
#main-banner img {
width: 100% !important;
height: 400px !important;
object-fit: cover !important;
display: block !important;
transform: translate(60px) !important;
}
.tabs {
margin-top: -25px !important;
}
/* General sections */
.card,
.about-section,
.method-section,
.explore-section,
.team-card {
background: #f7f3ec !important;
color: #4a3a32 !important;
padding: 25px;
border-radius: 18px;
box-shadow: none !important;
margin-bottom: 20px;
}
.about-section,
.method-section,
.explore-section {
max-width: 980px;
margin: 0 auto;
padding: 34px 28px 20px 28px;
}
.about-kicker {
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 13px;
color: #9a6a45;
margin-bottom: 10px;
font-weight: 600;
}
.about-section h2,
.method-section h2,
.explore-section h2,
.card h2,
.team-card h2 {
color: #8a2a22 !important;
font-weight: 700 !important;
font-size: 30px;
margin-top: 8px;
margin-bottom: 18px;
}
.about-lead {
font-size: 18px;
line-height: 1.6;
margin-bottom: 14px;
}
.about-section p,
.method-section p,
.explore-section p {
line-height: 1.65;
margin-bottom: 18px;
}
.about-question {
font-family: Georgia, serif;
font-size: 18px;
line-height: 1.45;
color: #5a2d27;
margin: 38px 0 70px 0;;
padding-left: 22px;
border-left: 4px solid #8a2a22;
}
.about-note,
.about-footer {
text-align: center;
color: #6a574e;
font-size: 15px;
max-width: 760px;
margin: 30px auto 0 auto;
background: #f7f3ec !important;
border: none !important;
box-shadow: none !important;
}
/* About feature blocks */
.feature-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 22px 36px;
margin-top: 34px;
margin-bottom: 34px;
}
.feature-card {
background: transparent !important;
border: none !important;
box-shadow: none !important;
padding: 0;
}
.feature-title {
color: #8a2a22;
font-weight: 700;
margin-bottom: 8px;
font-size: 17px;
}
.feature-text {
color: #4a3a32;
font-size: 15px;
line-height: 1.55;
}
/* Method */
.method-steps {
margin-top: 32px;
display: flex;
flex-direction: column;
gap: 18px;
}
.method-step {
display: grid;
grid-template-columns: 48px 1fr;
gap: 18px;
align-items: start;
}
.step-number {
width: 38px;
height: 38px;
border-radius: 50%;
border: 2px solid rgba(138, 42, 34, 0.35);
color: #8a2a22;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-family: Georgia, serif;
}
.step-title {
color: #8a2a22;
font-weight: 700;
font-size: 17px;
margin-bottom: 5px;
}
.step-text {
color: #4a3a32;
font-size: 15px;
line-height: 1.55;
}
/* Explore alignments */
.reading-view {
max-width: 1080px;
margin: 18px auto 34px auto;
}
.segment-heading {
font-family: Georgia, serif;
font-size: 20px;
color: #5a2d27;
margin-bottom: 18px;
}
.segment-heading span {
color: #8a2a22;
font-weight: 700;
}
.main-witness-card {
background: #fbf8f3;
border-left: 5px solid #8a2a22;
padding: 22px 24px;
margin-bottom: 28px;
border-radius: 0 16px 16px 0;
}
.witness-label {
color: #8a2a22;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: 8px;
}
.witness-section-title {
color: #8a2a22;
font-weight: 700;
font-size: 18px;
margin: 24px 0 10px 0;
}
.parallel-view {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 18px;
margin-top: 18px;
margin-bottom: 28px;
}
.witness-card {
background: #f7f3ec;
border-left: 3px solid rgba(138, 42, 34, 0.28);
padding: 16px 18px;
min-height: 120px;
}
.witness-meta {
color: #9a6a45;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 12px;
}
.main-witness-text {
color: #2f2824;
font-family: Georgia, serif;
font-size: 20px;
line-height: 1.7;
}
.witness-text {
color: #2f2824;
font-family: Georgia, serif;
font-size: 17px;
line-height: 1.6;
}
.raw-warning {
background: #fbf8f3;
border-left: 4px solid #9a6a45;
padding: 14px 18px;
margin-bottom: 22px;
color: #5a4a42;
font-size: 15px;
line-height: 1.5;
}
.corpus-notice {
max-width: 980px;
margin: 0 auto 20px auto;
padding: 16px 20px;
background: #fbf8f3;
border-left: 4px solid rgba(138, 42, 34, 0.45);
color: #4a3a32;
font-size: 15px;
line-height: 1.5;
}
.search-status {
max-width: 980px;
margin: 8px auto 20px auto;
padding: 12px 16px;
background: #fbf8f3;
border-left: 4px solid #9a6a45;
color: #5a4a42;
font-size: 14px;
line-height: 1.5;
}
.report-box {
margin-top: 12px;
padding: 12px 16px;
background: #fbf8f3;
border-left: 4px solid rgba(138, 42, 34, 0.35);
}
.report-box a {
color: #8a2a22 !important;
font-weight: 700;
text-decoration: none;
}
.report-box a:hover {
text-decoration: underline;
}
/* Gradio form controls */
.gradio-container label,
.gradio-container .wrap label,
.gradio-container span {
color: #8a2a22 !important;
}
.gradio-container input,
.gradio-container textarea,
.gradio-container select {
background: #f7f3ec !important;
color: #2b241f !important;
border-color: rgba(138, 42, 34, 0.18) !important;
}
.gradio-container .block,
.gradio-container .form,
.gradio-container .form > *,
.gradio-container [data-testid="block-info"],
.gradio-container [data-testid="dropdown"] {
background: #f7f3ec !important;
border-color: rgba(138, 42, 34, 0.12) !important;
}
.gradio-container button {
background: #f7f3ec !important;
color: #8a2a22 !important;
border-color: rgba(138, 42, 34, 0.18) !important;
}
/* Witness checkbox chips */
.gradio-container input[type="checkbox"] {
accent-color: #8a2a22 !important;
}
.gradio-container label:has(input[type="checkbox"]) {
background: #f7f3ec !important;
color: #4a3a32 !important;
border: 1px solid rgba(138, 42, 34, 0.18) !important;
border-radius: 10px !important;
}
.gradio-container label:has(input[type="checkbox"]:checked) {
background: #fbf8f3 !important;
color: #8a2a22 !important;
border: 1px solid rgba(138, 42, 34, 0.35) !important;
}
/* Team */
.person-avatar {
width: 110px !important;
height: 110px !important;
min-height: 110px !important;
margin: auto !important;
display: block !important;
}
.person-avatar img {
width: 110px !important;
height: 110px !important;
min-height: 110px !important;
object-fit: cover !important;
border-radius: 50% !important;
border: 3px solid rgba(138, 42, 34, 0.25);
display: block !important;
}
.person-avatar .icon-button-wrapper,
.person-avatar .image-controls,
.person-avatar [aria-label="Download"],
.person-avatar [aria-label="Fullscreen"],
.person-avatar [title="Download"],
.person-avatar [title="Fullscreen"] {
display: none !important;
}
.person-name {
color: #8a2a22;
font-weight: 700;
font-size: 17px;
margin-top: 12px;
margin-bottom: 8px;
text-align: center;
}
.person-links {
font-size: 14px;
text-align: center;
}
.person-links a {
color: #8a2a22 !important;
text-decoration: none;
font-weight: 600;
}
.person-links a:hover {
text-decoration: underline;
}
/* Contact */
.contact-section {
max-width: 980px;
margin: 0 auto;
padding: 34px 28px 20px 28px;
color: #4a3a32;
}
.contact-section h2 {
color: #8a2a22 !important;
font-weight: 700 !important;
font-size: 30px;
margin-top: 8px;
margin-bottom: 18px;
}
.contact-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 22px;
margin-top: 30px;
margin-bottom: 28px;
}
.contact-card {
background: #f7f3ec;
border-left: 3px solid rgba(138, 42, 34, 0.35);
padding: 18px;
}
.contact-title {
color: #8a2a22;
font-weight: 700;
font-size: 17px;
margin-bottom: 8px;
}
.contact-text {
color: #4a3a32;
font-size: 15px;
line-height: 1.55;
margin-bottom: 12px;
}
.contact-link a {
color: #8a2a22 !important;
font-weight: 700;
text-decoration: none;
}
.contact-link a:hover {
text-decoration: underline;
}
/* Publications — sober style */
.publications-section {
max-width: 980px;
margin: 0 auto;
padding: 34px 28px 20px 28px;
color: #4a3a32;
}
.publications-section h2 {
color: #8a2a22 !important;
font-weight: 700 !important;
font-size: 30px;
margin-top: 8px;
margin-bottom: 18px;
}
.publication-list {
display: flex;
flex-direction: column;
gap: 22px;
margin-top: 32px;
}
.publication-item {
background: transparent !important;
border-left: 2px solid rgba(138, 42, 34, 0.22);
padding: 4px 0 4px 18px;
}
.publication-title {
color: #5a2d27;
font-weight: 650;
font-size: 17px;
line-height: 1.45;
margin-bottom: 6px;
}
.publication-meta {
color: #5a4a42;
font-size: 14px;
font-weight: 400;
line-height: 1.55;
margin-bottom: 8px;
}
.publication-text {
color: #6a574e;
font-size: 14px;
line-height: 1.5;
margin-bottom: 8px;
}
.publication-link {
margin-top: 6px;
}
.publication-link a {
color: #8a2a22 !important;
font-size: 14px;
font-weight: 600;
text-decoration: none;
}
.publication-link a:hover {
text-decoration: underline;
}
.bibtex-box {
margin-top: 8px;
background: transparent !important;
border-left: none !important;
padding: 0;
}
.bibtex-box summary {
cursor: pointer;
color: #9a6a45;
font-size: 13px;
font-weight: 600;
margin-top: 6px;
}
.bibtex-box pre {
white-space: pre-wrap;
color: #4a3a32;
font-size: 12px;
line-height: 1.45;
background: #fbf8f3;
border: 1px solid rgba(138, 42, 34, 0.10);
border-radius: 10px;
padding: 12px;
margin: 10px 0 0 0;
}
/* Repositories */
.repository-list {
margin-top: 42px;
padding-top: 26px;
}
.repository-title {
color: #8a2a22;
font-weight: 700;
font-size: 22px;
margin-bottom: 18px;
}
.repository-group-title {
color: #9a6a45;
font-weight: 700;
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.08em;
margin-top: 26px;
margin-bottom: 10px;
}
.repository-item {
border-left: 2px solid rgba(138, 42, 34, 0.20);
padding: 4px 0 4px 18px;
margin-bottom: 18px;
color: #5a4a42;
font-size: 14px;
line-height: 1.55;
}
.repository-item strong {
color: #5a2d27;
font-size: 15px;
}
.repository-links {
margin-top: 6px;
}
.repository-item a,
.repository-links a {
color: #8a2a22 !important;
font-size: 14px;
font-weight: 600;
text-decoration: none;
}
.repository-item a:hover,
.repository-links a:hover {
text-decoration: underline;
}
/* Strong mobile fixes */
@media screen and (max-width: 768px) {
html,
body,
#root,
.gradio-container {
width: 100% !important;
max-width: 100% !important;
overflow-x: hidden !important;
}
.gradio-container {
margin: 0 !important;
padding: 0 12px !important;
}
.contain,
.wrap,
.main,
.app,
main {
width: 100% !important;
max-width: 100% !important;
overflow-x: hidden !important;
}
#main-banner {
width: 100% !important;
height: auto !important;
margin: 0 0 12px 0 !important;
overflow: hidden !important;
}
#main-banner img {
width: 100% !important;
height: auto !important;
max-height: 220px !important;
object-fit: contain !important;
transform: none !important;
}
.tabs {
width: 100% !important;
max-width: 100% !important;
overflow-x: auto !important;
white-space: nowrap !important;
margin-top: 0 !important;
}
.about-section,
.method-section,
.explore-section,
.publications-section,
.contact-section,
.card,
.team-card,
.reading-view {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
padding: 22px 8px !important;
overflow-x: hidden !important;
}
.about-section h2,
.method-section h2,
.explore-section h2,
.publications-section h2,
.contact-section h2 {
font-size: 25px !important;
line-height: 1.2 !important;
}
.about-kicker {
font-size: 11px !important;
letter-spacing: 0.06em !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
}
.about-lead,
.about-section p,
.method-section p,
.explore-section p,
.step-text,
.feature-text,
.publication-meta,
.publication-text,
.repository-item {
font-size: 15px !important;
line-height: 1.55 !important;
max-width: 100% !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
.about-question {
font-size: 18px !important;
line-height: 1.45 !important;
margin: 26px 0 36px 0 !important;
padding-left: 14px !important;
max-width: 100% !important;
overflow-wrap: anywhere !important;
}
.feature-grid,
.parallel-view,
.contact-grid,
.team-grid {
display: grid !important;
grid-template-columns: 1fr !important;
gap: 16px !important;
width: 100% !important;
max-width: 100% !important;
}
.method-step {
display: grid !important;
grid-template-columns: 44px minmax(0, 1fr) !important;
gap: 12px !important;
width: 100% !important;
max-width: 100% !important;
}
.step-title {
font-size: 16px !important;
line-height: 1.35 !important;
overflow-wrap: anywhere !important;
}
.step-number {
width: 36px !important;
height: 36px !important;
min-width: 36px !important;
}
.main-witness-card,
.witness-card,
.publication-item,
.repository-item,
.corpus-notice,
.search-status {
width: 100% !important;
max-width: 100% !important;
overflow-x: hidden !important;
overflow-wrap: anywhere !important;
}
.main-witness-text {
font-size: 17px !important;
line-height: 1.55 !important;
overflow-wrap: anywhere !important;
}
.witness-text {
font-size: 16px !important;
line-height: 1.5 !important;
overflow-wrap: anywhere !important;
}
.bibtex-box pre {
max-width: 100% !important;
overflow-x: auto !important;
white-space: pre-wrap !important;
word-break: break-word !important;
font-size: 11px !important;
}
.gradio-container table,
.gradio-container .dataframe,
.gradio-container [data-testid="dataframe"] {
max-width: 100% !important;
overflow-x: auto !important;
}
}
/* Ultra-strong mobile width fix */
@media screen and (max-width: 768px) {
* {
box-sizing: border-box !important;
}
html,
body,
#root,
.gradio-container,
.app,
.main,
.wrap,
.contain,
main {
width: 100vw !important;
max-width: 100vw !important;
min-width: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
overflow-x: hidden !important;
}
.gradio-container > *,
.app > *,
.main > *,
.wrap > *,
.contain > *,
main > * {
max-width: 100% !important;
min-width: 0 !important;
}
.tabs,
[role="tablist"] {
width: 100% !important;
max-width: 100% !important;
overflow-x: auto !important;
white-space: nowrap !important;
}
.about-section,
.method-section,
.explore-section,
.publications-section,
.contact-section,
.team-card,
.card {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 14px !important;
padding-right: 14px !important;
}
.about-section *,
.method-section *,
.explore-section *,
.publications-section *,
.contact-section *,
.team-card *,
.card * {
max-width: 100% !important;
min-width: 0 !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
.method-step {
display: grid !important;
grid-template-columns: 38px minmax(0, 1fr) !important;
width: 100% !important;
max-width: 100% !important;
}
.step-title,
.step-text,
.about-lead,
.about-question {
white-space: normal !important;
overflow-wrap: anywhere !important;
}
.feature-grid,
.parallel-view,
.team-grid,
.contact-grid {
grid-template-columns: 1fr !important;
width: 100% !important;
max-width: 100% !important;
}
#main-banner,
#main-banner img {
width: 100% !important;
max-width: 100% !important;
transform: none !important;
}
}
/* Emergency mobile width reset */
@media screen and (max-width: 768px) {
html,
body {
width: 100% !important;
max-width: 100% !important;
overflow-x: hidden !important;
margin: 0 !important;
padding: 0 !important;
}
#root,
.gradio-container,
.gradio-container > div,
.gradio-container .main,
.gradio-container .wrap,
.gradio-container .contain,
.gradio-container .block,
.gradio-container .form,
.gradio-container .gap,
.gradio-container .panel,
.gradio-container .tabs,
.gradio-container .tabitem,
.gradio-container .tab-nav,
.gradio-container [class*="container"],
.gradio-container [class*="wrap"],
.gradio-container [class*="block"],
.gradio-container [class*="column"],
.gradio-container [class*="row"] {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
overflow-x: hidden !important;
box-sizing: border-box !important;
}
.gradio-container [role="tablist"] {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
overflow-x: auto !important;
overflow-y: hidden !important;
white-space: nowrap !important;
display: flex !important;
flex-wrap: nowrap !important;
}
.gradio-container [role="tab"] {
flex: 0 0 auto !important;
max-width: none !important;
white-space: nowrap !important;
}
.about-section,
.method-section,
.explore-section,
.publications-section,
.contact-section,
.team-card,
.card {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
margin: 0 !important;
padding: 18px 8px !important;
box-sizing: border-box !important;
overflow-x: hidden !important;
}
.about-section *,
.method-section *,
.explore-section *,
.publications-section *,
.contact-section *,
.team-card *,
.card * {
max-width: 100% !important;
min-width: 0 !important;
box-sizing: border-box !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
.method-step {
display: grid !important;
grid-template-columns: 32px minmax(0, 1fr) !important;
gap: 10px !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.step-number {
width: 30px !important;
height: 30px !important;
min-width: 30px !important;
font-size: 13px !important;
}
.about-lead,
.step-text,
.step-title,
.feature-text,
.main-witness-text,
.witness-text,
.publication-title,
.publication-meta,
.publication-text,
.repository-item {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
}
.about-lead {
font-size: 15px !important;
line-height: 1.5 !important;
}
.step-title {
font-size: 15px !important;
line-height: 1.3 !important;
}
.step-text {
font-size: 14px !important;
line-height: 1.45 !important;
}
.about-section h2,
.method-section h2,
.explore-section h2,
.publications-section h2,
.contact-section h2 {
font-size: 24px !important;
line-height: 1.15 !important;
}
.about-kicker {
font-size: 10px !important;
line-height: 1.4 !important;
letter-spacing: 0.04em !important;
white-space: normal !important;
}
.feature-grid,
.parallel-view,
.team-grid,
.contact-grid {
display: grid !important;
grid-template-columns: minmax(0, 1fr) !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
#main-banner {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
margin: 0 0 10px 0 !important;
overflow: hidden !important;
}
#main-banner img {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
max-height: 200px !important;
object-fit: contain !important;
transform: none !important;
}
iframe,
table,
pre,
code,
.dataframe,
[data-testid="dataframe"] {
max-width: 100% !important;
overflow-x: auto !important;
white-space: pre-wrap !important;
}
}
/* Professional mobile tabs: scroll tabs, not the whole page */
@media screen and (max-width: 768px) {
.gradio-container [role="tablist"] {
display: flex !important;
flex-wrap: nowrap !important;
overflow-x: auto !important;
overflow-y: hidden !important;
max-width: 100vw !important;
width: 100% !important;
white-space: nowrap !important;
scrollbar-width: thin !important;
}
.gradio-container [role="tab"] {
flex: 0 0 auto !important;
white-space: nowrap !important;
max-width: none !important;
}
.gradio-container [role="tabpanel"] {
width: 100% !important;
max-width: 100vw !important;
overflow-x: hidden !important;
}
.gradio-container {
overflow-x: hidden !important;
}
}
/* Final responsive fix */
@media screen and (max-width: 768px) {
html,
body,
gradio-app,
#root,
.gradio-container {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
overflow-x: hidden !important;
}
.gradio-container * {
max-width: 100% !important;
min-width: 0 !important;
box-sizing: border-box !important;
}
.gradio-container .row,
.gradio-container .column,
.gradio-container [class*="row"],
.gradio-container [class*="column"] {
flex-wrap: wrap !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.gradio-container [data-testid="block-label"],
.gradio-container label,
.gradio-container input,
.gradio-container textarea,
.gradio-container select,
.gradio-container button {
max-width: 100% !important;
min-width: 0 !important;
white-space: normal !important;
}
.about-section,
.method-section,
.explore-section,
.publications-section,
.contact-section,
.team-card,
.card,
.reading-view,
.main-witness-card,
.witness-card,
.publication-item,
.repository-item {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 10px !important;
padding-right: 10px !important;
overflow-x: hidden !important;
}
.feature-grid,
.parallel-view,
.team-grid,
.contact-grid {
display: grid !important;
grid-template-columns: minmax(0, 1fr) !important;
width: 100% !important;
max-width: 100% !important;
}
.method-step {
grid-template-columns: 32px minmax(0, 1fr) !important;
}
.about-section *,
.method-section *,
.explore-section *,
.publications-section *,
.contact-section *,
.team-card *,
.card *,
.reading-view * {
white-space: normal !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
[role="tablist"] {
max-width: 100% !important;
overflow-x: auto !important;
display: flex !important;
flex-wrap: nowrap !important;
}
[role="tab"] {
flex: 0 0 auto !important;
white-space: nowrap !important;
}
#main-banner img {
transform: none !important;
width: 100% !important;
height: auto !important;
object-fit: contain !important;
}
}/* Android / tablet responsive fix */
@media screen and (max-width: 1200px) {
html,
body,
#root,
.gradio-container {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
margin: 0 !important;
padding: 0 !important;
overflow-x: hidden !important;
}
.gradio-container {
padding-left: 12px !important;
padding-right: 12px !important;
}
.gradio-container *,
.gradio-container *::before,
.gradio-container *::after {
box-sizing: border-box !important;
min-width: 0 !important;
}
#main-banner {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
margin: 0 0 14px 0 !important;
overflow: hidden !important;
}
#main-banner img {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
max-height: 220px !important;
object-fit: contain !important;
transform: none !important;
display: block !important;
}
.tabs,
[role="tablist"] {
width: 100% !important;
max-width: 100% !important;
overflow-x: auto !important;
overflow-y: hidden !important;
display: flex !important;
flex-wrap: nowrap !important;
white-space: nowrap !important;
}
[role="tab"] {
flex: 0 0 auto !important;
white-space: nowrap !important;
}
.about-section,
.method-section,
.explore-section,
.publications-section,
.contact-section,
.team-card,
.card,
.reading-view,
.main-witness-card,
.witness-card,
.publication-item,
.repository-item,
.corpus-notice,
.search-status {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 12px !important;
padding-right: 12px !important;
overflow-x: hidden !important;
}
.about-section *,
.method-section *,
.explore-section *,
.publications-section *,
.contact-section *,
.team-card *,
.card *,
.reading-view * {
max-width: 100% !important;
min-width: 0 !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
.feature-grid,
.parallel-view,
.team-grid,
.contact-grid {
display: grid !important;
grid-template-columns: minmax(0, 1fr) !important;
gap: 16px !important;
width: 100% !important;
max-width: 100% !important;
}
.method-step {
display: grid !important;
grid-template-columns: 36px minmax(0, 1fr) !important;
gap: 12px !important;
width: 100% !important;
max-width: 100% !important;
}
.step-number {
width: 32px !important;
height: 32px !important;
min-width: 32px !important;
font-size: 14px !important;
}
.about-section h2,
.method-section h2,
.explore-section h2,
.publications-section h2,
.contact-section h2 {
font-size: 26px !important;
line-height: 1.2 !important;
}
.about-kicker {
font-size: 11px !important;
letter-spacing: 0.05em !important;
white-space: normal !important;
}
.about-lead,
.about-question,
.step-title,
.step-text,
.feature-text,
.main-witness-text,
.witness-text,
.publication-title,
.publication-meta,
.publication-text,
.repository-item {
max-width: 100% !important;
white-space: normal !important;
overflow-wrap: anywhere !important;
word-break: normal !important;
}
.about-lead {
font-size: 16px !important;
line-height: 1.5 !important;
}
.step-title {
font-size: 16px !important;
line-height: 1.3 !important;
}
.step-text {
font-size: 15px !important;
line-height: 1.5 !important;
}
.main-witness-text {
font-size: 17px !important;
line-height: 1.55 !important;
}
.witness-text {
font-size: 16px !important;
line-height: 1.5 !important;
}
.bibtex-box pre,
table,
.dataframe,
[data-testid="dataframe"] {
max-width: 100% !important;
overflow-x: auto !important;
white-space: pre-wrap !important;
}
}
.mobile-warning {
display: none;
}
@media screen and (max-width: 1200px) {
.mobile-warning {
display: block !important;
background: #fbf8f3;
border-left: 3px solid rgba(138, 42, 34, 0.35);
color: #5a2d27;
padding: 14px 16px;
margin: 14px 12px 20px 12px;
font-size: 14px;
line-height: 1.5;
}
}
"""
mobile_head = """
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
"""
#with gr.Blocks(
# title="Aquilign Demo",
# theme=gr.themes.Soft(),
# css=custom_css,
#) as demo:
with gr.Blocks(
title="Aquilign Demo",
theme=gr.themes.Soft(),
css=custom_css,
head=mobile_head,
) as demo:
gr.Image(
value="quilign.png",
show_label=False,
container=False,
elem_id="main-banner",
)
gr.HTML(
"""
<div class="mobile-warning">
Aquilign Explorer is currently optimized for laptop and desktop screens.
The mobile layout is experimental and may not display all alignment views correctly.
</div>
"""
)
with gr.Tabs():
with gr.Tab("Explore alignments"):
default_corpus = "Lancelot"
default_type = "Reviewed"
segments = get_segment_choices(default_corpus, default_type)
first_segment = segments[0] if segments else None
witnesses = get_witness_choices(default_corpus, default_type)
gr.Markdown(
"""
<div class="explore-section">
<div class="about-kicker">Browse · Compare · Inspect</div>
<h2>Explore alignments</h2>
<p class="about-lead">
Browse curated alignment samples produced with Aquilign and manually reviewed
for demonstration purposes. You can search across witnesses, choose which witnesses
to display, and inspect raw Aquilign output for transparency.
</p>
</div>
"""
)
notice_output = gr.HTML(
value=corpus_notice(default_corpus, default_type)
)
with gr.Row():
corpus_selector = gr.Dropdown(
choices=list(DATASETS.keys()),
value=default_corpus,
label="Corpus",
interactive=True,
)
type_selector = gr.Dropdown(
choices=["Reviewed", "Raw"],
value=default_type,
label="Alignment type",
interactive=True,
)
with gr.Row():
search_box = gr.Textbox(
label="Search in witnesses",
placeholder="Search a word or expression, then press Enter...",
)
clear_button = gr.Button("Clear search")
search_status = gr.HTML(value="")
witness_selector = gr.CheckboxGroup(
choices=witnesses,
value=witnesses,
label="Witnesses to display",
interactive=True,
)
with gr.Row():
previous_button = gr.Button("Previous segment")
next_button = gr.Button("Next segment")
segment_selector = gr.Dropdown(
choices=segments,
value=first_segment,
label="Aligned segment",
interactive=True,
)
parallel_output = gr.HTML(
value=view_segment(default_corpus, default_type, first_segment, witnesses)
if first_segment else "<p>No data found.</p>"
)
with gr.Row():
download_button = gr.DownloadButton(
label="Download current dataset",
value=str(get_data_file(default_corpus, default_type)),
)
with gr.Accordion("Report an alignment issue", open=False):
issue_text = gr.Textbox(
label="Issue report template",
value=make_issue_report(default_corpus, default_type, first_segment)
if first_segment else "",
lines=7,
interactive=True,
)
issue_link = gr.HTML(
value=make_github_issue_link(default_corpus, default_type, first_segment)
if first_segment else ""
)
with gr.Accordion("Full alignment table", open=False):
table = gr.Dataframe(
value=load_alignments(default_corpus, default_type),
interactive=False,
)
corpus_selector.change(
fn=update_explore,
inputs=[corpus_selector, type_selector],
outputs=[
segment_selector,
search_box,
witness_selector,
parallel_output,
table,
notice_output,
download_button,
issue_text,
issue_link,
],
)
type_selector.change(
fn=update_explore,
inputs=[corpus_selector, type_selector],
outputs=[
segment_selector,
search_box,
witness_selector,
parallel_output,
table,
notice_output,
download_button,
issue_text,
issue_link,
],
)
search_box.submit(
fn=update_search,
inputs=[corpus_selector, type_selector, search_box, witness_selector],
outputs=[
segment_selector,
parallel_output,
search_status,
issue_text,
issue_link,
],
)
clear_button.click(
fn=clear_search,
inputs=[corpus_selector, type_selector, witness_selector],
outputs=[
search_box,
segment_selector,
parallel_output,
search_status,
issue_text,
issue_link,
],
)
previous_button.click(
fn=lambda corpus, atype, segment, query, witnesses: move_segment(
corpus, atype, segment, query, witnesses, "previous"
),
inputs=[
corpus_selector,
type_selector,
segment_selector,
search_box,
witness_selector,
],
outputs=[
segment_selector,
parallel_output,
issue_text,
issue_link,
],
)
next_button.click(
fn=lambda corpus, atype, segment, query, witnesses: move_segment(
corpus, atype, segment, query, witnesses, "next"
),
inputs=[
corpus_selector,
type_selector,
segment_selector,
search_box,
witness_selector,
],
outputs=[
segment_selector,
parallel_output,
issue_text,
issue_link,
],
)
segment_selector.change(
fn=view_segment,
inputs=[
corpus_selector,
type_selector,
segment_selector,
witness_selector,
],
outputs=parallel_output,
)
segment_selector.change(
fn=make_issue_report,
inputs=[corpus_selector, type_selector, segment_selector],
outputs=issue_text,
)
segment_selector.change(
fn=make_github_issue_link,
inputs=[corpus_selector, type_selector, segment_selector],
outputs=issue_link,
)
witness_selector.change(
fn=view_segment,
inputs=[
corpus_selector,
type_selector,
segment_selector,
witness_selector,
],
outputs=parallel_output,
)
with gr.Tab("About"):
gr.Markdown(
"""
<div class="about-section">
<div class="about-kicker">Digital philology · Medieval texts · Multilingual alignment</div>
<h2>About Aquilign</h2>
<div class="about-question">
How can we align multilingual medieval textual traditions while preserving
their variation, transmission history, and philological complexity?
</div>
<p>
Aquilign is a multilingual alignment and collation engine
for historical and philological corpora.
It is designed to help researchers compare medieval textual traditions across
languages, witnesses, translations, and corpora.
</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-title">Explore traditions</div>
<div class="feature-text">
Compare related textual traditions across manuscripts, languages, and versions.
</div>
</div>
<div class="feature-card">
<div class="feature-title">Align across languages</div>
<div class="feature-text">
Work with medieval Romance languages, Latin, Middle English, and other historical corpora.
</div>
</div>
<div class="feature-card">
<div class="feature-title">Discover connections</div>
<div class="feature-text">
Identify corresponding passages even when phrasing or structure diverges.
</div>
</div>
<div class="feature-card">
<div class="feature-title">Multiple witnesses & corpora</div>
<div class="feature-text">
Support research on translation, transmission, collation, and stemmatological analysis.
</div>
</div>
</div>
<p class="about-note">
Aquilign is developed within the ProMeText ecosystem and released as an
open-source research tool for computational humanities, historical linguistics,
and historical NLP.
</p>
</div>
"""
)
with gr.Tab("Method"):
gr.Markdown(
"""
<div class="method-section">
<div class="about-kicker">Workflow · Segmentation · Alignment · Exploration</div>
<h2>Method</h2>
<p class="about-lead">
Aquilign is designed as a modular workflow for preparing, segmenting,
aligning, and exploring multilingual medieval textual traditions.
</p>
<div class="method-steps">
<div class="method-step">
<div class="step-number">1</div>
<div>
<div class="step-title">Prepare the texts</div>
<div class="step-text">
Input texts are gathered, cleaned, and organised by language, witness,
or textual tradition.
</div>
</div>
</div>
<div class="method-step">
<div class="step-number">2</div>
<div>
<div class="step-title">Segment into comparable units</div>
<div class="step-text">
Texts are divided into phrase-level units or clauses,
so that corresponding passages can be compared across witnesses and languages.
</div>
</div>
</div>
<div class="method-step">
<div class="step-number">3</div>
<div>
<div class="step-title">Align across languages and witnesses</div>
<div class="step-text">
Aquilign identifies related passages across texts that may differ through
translation, omission, expansion, rephrasing, or structural variation.
</div>
</div>
</div>
<div class="method-step">
<div class="step-number">4</div>
<div>
<div class="step-title">Export reusable results</div>
<div class="step-text">
Alignment results can be exported for inspection, correction, reuse,
or integration into further philological and computational workflows.
</div>
</div>
</div>
<div class="method-step">
<div class="step-number">5</div>
<div>
<div class="step-title">Review and curate</div>
<div class="step-text">
Automatic alignments may be inspected and corrected by researchers before
being presented as reviewed scholarly data.
</div>
</div>
</div>
<div class="method-step">
<div class="step-number">6</div>
<div>
<div class="step-title">Explore and share</div>
<div class="step-text">
This interface focuses on making the results readable, navigable,
and easier to share with researchers and collaborators.
</div>
</div>
</div>
</div>
<p class="about-note">
The current demo presents reviewed alignment samples alongside raw Aquilign output
for transparency.
</p>
</div>
"""
)
with gr.Tab("Publications"):
gr.Markdown(
"""
<div class="publications-section">
<div class="about-kicker">Publications · Citation · Datasets</div>
<h2>Publications</h2>
<p class="about-lead">
Please cite the relevant publications and datasets when using Aquilign,
the demo alignments, or the associated segmentation resources.
</p>
<div class="publication-list">
<div class="publication-item">
<div class="publication-title">
Textual Transmission without Borders: Multiple Multilingual Alignment and Stemmatology of the <em>Lancelot en prose</em> (Medieval French, Castilian, Italian)
</div>
<div class="publication-meta">
Gille Levenson, M., Ing, L., & Camps, J.-B. (2024). In <em>Proceedings of the Computational Humanities Research Conference 2024</em>, CEUR Workshop Proceedings, Vol. 3834, pp. 65–92.
</div>
<div class="publication-text">
Main publication presenting multilingual alignment and stemmatological analysis of the <em>Lancelot en prose</em> textual tradition.
</div>
<div class="publication-link">
<a href="https://ceur-ws.org/Vol-3834/#paper104" target="_blank">Paper</a>
</div>
<details class="bibtex-box">
<summary>BibTeX</summary>
<pre>
@inproceedings{gillelevenson_TextualTransmissionBorders_2024a,
title = {Textual Transmission without Borders: Multiple Multilingual Alignment and Stemmatology of the ``Lancelot En Prose'' (Medieval French, Castilian, Italian)},
shorttitle = {Textual Transmission without Borders},
booktitle = {Proceedings of the Computational Humanities Research Conference 2024},
author = {Gille Levenson, Matthias and Ing, Lucence and Camps, Jean-Baptiste},
editor = {Haverals, Wouter and Koolen, Marijn and Thompson, Laure},
date = {2024},
series = {CEUR Workshop Proceedings},
volume = {3834},
pages = {65--92},
publisher = {CEUR},
location = {Aarhus, Denmark},
issn = {1613-0073},
url = {https://ceur-ws.org/Vol-3834/#paper104},
urldate = {2024-12-09},
eventtitle = {Computational Humanities Research 2024},
langid = {english}
}
</pre>
</details>
</div>
<div class="publication-item">
<div class="publication-title">
Phrase-Level Segmentation on Medieval Corpora for Aligning Multilingual Texts
</div>
<div class="publication-meta">
Ing, L., Gille Levenson, M., & Macedo, C. (2026). In <em>Proceedings of the Fifteenth Language Resources and Evaluation Conference (LREC 2026)</em>.
</div>
<div class="publication-text">
Publication describing the phrase-level segmentation workflow and dataset used for aligning multilingual medieval corpora.
</div>
<div class="publication-link">
<a href="https://doi.org/10.63317/32HUZUUOKPFR" target="_blank">Paper</a>
</div>
<details class="bibtex-box">
<summary>BibTeX</summary>
<pre>
@inproceedings{ing2026phrase,
title = {Phrase-Level Segmentation on Medieval Corpora for Aligning Multilingual Texts},
author = {Ing, Lucence and Gille Levenson, Matthias and Macedo, Carolina},
booktitle = {Proceedings of the Fifteenth Language Resources and Evaluation Conference (LREC 2026)},
year = {2026},
doi = {10.63317/32HUZUUOKPFR}
}
</pre>
</details>
</div>
</div>
<div class="repository-list">
<div class="repository-title">Resources and repositories</div>
<div class="repository-group-title">Tool</div>
<div class="repository-item">
<strong>Aquilign</strong><br>
Main multilingual alignment and collation tool.<br>
<div class="repository-links">
<a href="https://github.com/ProMeText/Aquilign" target="_blank">Aquilign repository</a>
</div>
</div>
<div class="repository-group-title">Demo corpora</div>
<div class="repository-item">
<strong>Lancelot par maints langages</strong><br>
Demo corpus for the <em>Lancelot en prose</em> tradition.<br>
<div class="repository-links">
<a href="https://github.com/ProMeText/lancelot-par-maints-langages" target="_blank">Lancelot repository</a>
</div>
</div>
<div class="repository-item">
<strong>Multilingual Aegidius</strong><br>
Demo corpus for <em>De regimine principum</em> and its multilingual transmission.<br>
<div class="repository-links">
<a href="https://github.com/ProMeText/Multilingual_Aegidius" target="_blank">Multilingual Aegidius repository</a>
</div>
</div>
<div class="repository-group-title">Datasets</div>
<div class="repository-item">
<strong>Multilingual Segmentation Dataset for Historical Prose</strong><br>
Dataset for phrase-level segmentation of historical prose, designed to support
the alignment of multilingual medieval textual traditions.<br>
<div class="repository-links">
<a href="https://github.com/ProMeText/multilingual-segmentation-dataset" target="_blank">Segmentation dataset repository</a><br>
<a href="https://doi.org/10.5281/zenodo.16992629" target="_blank">Zenodo DOI / archived dataset</a>
</div>
</div>
<div class="repository-item">
<strong>Parallel Corpus for Fine-Tuning LaBSE</strong><br>
Parallel multilingual corpus used to fine-tune LaBSE for sentence- and phrase-level
alignment of historical texts.<br>
<div class="repository-links">
<a href="https://github.com/ProMeText/parallelium-scriptures-alignment-dataset/tree/main" target="_blank">Fine-tuning corpus repository</a>
</div>
</div>
</div>
<p class="about-note">
These references document the alignment approach, segmentation workflow,
datasets, and corpus resources associated with Aquilign.
</p>
</div>
"""
)
with gr.Tab("Team"):
gr.Markdown(
"""
<div class="card team-card">
<div class="about-kicker">People behind the project</div>
## Our team
Aquilign is developed by a small interdisciplinary team working at the intersection
of medieval studies, philology, computational humanities, and multilingual NLP.
</div>
"""
)
with gr.Row():
with gr.Column():
gr.Image(
value="team/matthias.png",
show_label=False,
container=False,
elem_classes="person-avatar",
)
gr.Markdown(
"""
<div class="person-name">Matthias Gille Levenson</div>
<div class="person-links">
<a href="https://orcid.org/0000-0001-9488-5986" target="_blank">ORCID</a>
</div>
"""
)
with gr.Column():
gr.Image(
value="team/lucence.png",
show_label=False,
container=False,
elem_classes="person-avatar",
)
gr.Markdown(
"""
<div class="person-name">Lucence Ing</div>
<div class="person-links">
<a href="https://orcid.org/0000-0002-8742-3000" target="_blank">ORCID</a>
</div>
"""
)
with gr.Column():
gr.Image(
value="team/carola.png",
show_label=False,
container=False,
elem_classes="person-avatar",
)
gr.Markdown(
"""
<div class="person-name">Carolina Macedo</div>
<div class="person-links">
<a href="https://orcid.org/0009-0001-5972-0921" target="_blank">ORCID</a>
</div>
"""
)
gr.Markdown(
"""
<div class="about-footer">
Together, the team works toward making multilingual medieval textual traditions
easier to compare, explore, and share.
</div>
"""
)
with gr.Tab("Contact"):
gr.Markdown(
"""
<div class="contact-section">
<div class="about-kicker">Contact · Feedback · Collaboration</div>
<h2>Contact</h2>
<p class="about-lead">
For questions, feedback, or collaboration requests related to Aquilign,
you can contact the project team or open an issue on GitHub.
</p>
<div class="contact-grid">
<div class="contact-card">
<div class="contact-title">GitHub repository</div>
<div class="contact-text">
Use GitHub to report bugs, suggest improvements, or discuss alignment issues.
</div>
<div class="contact-link">
<a href="https://github.com/ProMeText/Aquilign" target="_blank">ProMeText/Aquilign</a>
</div>
</div>
<div class="contact-card">
<div class="contact-title">Report an alignment issue</div>
<div class="contact-text">
When reporting an issue, please include the corpus, alignment type, segment ID,
and a short description of the problem.
</div>
<div class="contact-link">
<a href="https://github.com/ProMeText/Aquilign/issues" target="_blank">Open GitHub issues</a>
</div>
</div>
<div class="contact-card">
<div class="contact-title">Collaboration and reuse</div>
<div class="contact-text">
For academic questions, reuse, or collaboration requests, please open a GitHub issue
with the relevant context. The team will follow up from there.
</div>
<div class="contact-link">
<a href="https://github.com/ProMeText/Aquilign/issues" target="_blank">Contact via GitHub</a>
</div>
</div>
</div>
<p class="about-note">
Aquilign is developed as an open-source research tool for digital philology,
historical linguistics, and computational humanities.
</p>
</div>
"""
)
#demo.launch(allowed_paths=["team", "sample_data"])
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
allowed_paths=["team", "sample_data"],
)