Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA. Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb, Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming, Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office (Collabora/OnlyOffice). Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort) sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
209 lines
5.5 KiB
CSS
209 lines
5.5 KiB
CSS
/*
|
||
* Textebene von PDF.js.
|
||
*
|
||
* Der Block unten ist **wörtlich** aus pdfjs-dist/web/pdf_viewer.css
|
||
* übernommen, nur die Klasse heißt `.pdf-textLayer` statt `.textLayer`.
|
||
*
|
||
* Beim ersten Anlauf hatte ich ihn von Hand gekürzt – „wir zeigen nur Seiten
|
||
* an, der Rest ist Werkzeugleiste". Dabei fielen drei Zeilen weg, die wie
|
||
* Kleinkram aussahen und keiner waren:
|
||
*
|
||
* --min-font-size / --text-scale-factor / scale(var(--min-font-size-inv))
|
||
*
|
||
* PDF.js misst beim ersten Aufbau, wie klein der Browser Schrift überhaupt
|
||
* noch darstellt (Chrome erzwingt eine Mindestgröße), setzt daraus
|
||
* `--min-font-size` und rechnet sie mit der Skalierung wieder heraus. Ohne
|
||
* dieses Paar wird jede kleine Schrift vom Browser aufgeblasen: Die
|
||
* unsichtbaren Textstücke sitzen dann größer da als die gemalten Buchstaben
|
||
* darunter – zu sehen erst beim **Markieren**, wo die Auswahl als grobe,
|
||
* versetzte Kästen über dem Text lag statt auf ihm.
|
||
*
|
||
* Deshalb hier nichts mehr von Hand kürzen. Was die Datei mitbringt, gehört
|
||
* zusammen; was wir zusätzlich wollen, steht darunter.
|
||
*/
|
||
|
||
.pdf-textLayer{
|
||
color-scheme:only light;
|
||
|
||
position:absolute;
|
||
text-align:initial;
|
||
inset:0;
|
||
overflow:clip;
|
||
opacity:1;
|
||
line-height:1;
|
||
letter-spacing:normal;
|
||
word-spacing:normal;
|
||
-webkit-text-size-adjust:none;
|
||
-moz-text-size-adjust:none;
|
||
text-size-adjust:none;
|
||
forced-color-adjust:none;
|
||
transform-origin:0 0;
|
||
caret-color:CanvasText;
|
||
z-index:0;
|
||
|
||
&.highlighting{
|
||
touch-action:none;
|
||
}
|
||
|
||
:is(span, br){
|
||
color:transparent;
|
||
position:absolute;
|
||
white-space:pre;
|
||
cursor:text;
|
||
transform-origin:0% 0%;
|
||
-webkit-user-select:text;
|
||
-moz-user-select:text;
|
||
user-select:text;
|
||
}
|
||
--min-font-size:1;
|
||
--text-scale-factor:calc(var(--total-scale-factor) * var(--min-font-size));
|
||
--min-font-size-inv:calc(1 / var(--min-font-size));
|
||
|
||
> :not(.markedContent),
|
||
.markedContent span:not(.markedContent){
|
||
z-index:1;
|
||
|
||
--font-height:0;
|
||
font-size:calc(var(--text-scale-factor) * var(--font-height));
|
||
|
||
--scale-x:1;
|
||
--rotate:0deg;
|
||
transform:rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv));
|
||
}
|
||
|
||
.markedContent{
|
||
display:contents;
|
||
}
|
||
|
||
span[role="img"]{
|
||
-webkit-user-select:none;
|
||
-moz-user-select:none;
|
||
user-select:none;
|
||
cursor:default;
|
||
}
|
||
|
||
.highlight{
|
||
--highlight-bg-color:rgb(180 0 170 / 0.25);
|
||
--highlight-selected-bg-color:rgb(0 100 0 / 0.25);
|
||
--highlight-backdrop-filter:none;
|
||
--highlight-selected-backdrop-filter:none;
|
||
|
||
@media screen and (forced-colors: active){
|
||
--highlight-bg-color:transparent;
|
||
--highlight-selected-bg-color:transparent;
|
||
--highlight-backdrop-filter:var(--hcm-highlight-filter);
|
||
--highlight-selected-backdrop-filter:var(
|
||
--hcm-highlight-selected-filter
|
||
);
|
||
}
|
||
|
||
margin:-1px;
|
||
padding:1px;
|
||
background-color:var(--highlight-bg-color);
|
||
backdrop-filter:var(--highlight-backdrop-filter);
|
||
border-radius:4px;
|
||
|
||
&.appended{
|
||
position:initial;
|
||
}
|
||
|
||
&.begin{
|
||
border-radius:4px 0 0 4px;
|
||
}
|
||
|
||
&.end{
|
||
border-radius:0 4px 4px 0;
|
||
}
|
||
|
||
&.middle{
|
||
border-radius:0;
|
||
}
|
||
|
||
&.selected{
|
||
background-color:var(--highlight-selected-bg-color);
|
||
backdrop-filter:var(--highlight-selected-backdrop-filter);
|
||
scroll-margin-top:50px;
|
||
}
|
||
}
|
||
|
||
::-moz-selection{
|
||
background:rgba(0 0 255 / 0.25);
|
||
background:color-mix(in srgb, AccentColor, transparent 50%);
|
||
color:transparent;
|
||
}
|
||
|
||
::selection{
|
||
background:rgba(0 0 255 / 0.25);
|
||
background:color-mix(in srgb, AccentColor, transparent 50%);
|
||
color:transparent;
|
||
}
|
||
|
||
&.selectionRendering{
|
||
::-moz-selection{
|
||
background:transparent;
|
||
color:transparent;
|
||
}
|
||
::selection{
|
||
background:transparent;
|
||
color:transparent;
|
||
}
|
||
}
|
||
br::-moz-selection{
|
||
background:transparent;
|
||
}
|
||
br::selection{
|
||
background:transparent;
|
||
}
|
||
|
||
.endOfContent{
|
||
display:block;
|
||
position:absolute;
|
||
inset:100% 0 0;
|
||
z-index:0;
|
||
cursor:default;
|
||
-webkit-user-select:none;
|
||
-moz-user-select:none;
|
||
user-select:none;
|
||
}
|
||
|
||
&.selecting .endOfContent{
|
||
top:0;
|
||
}
|
||
}
|
||
/*
|
||
* Fundstellen der Suche.
|
||
*
|
||
* Eine eigene Ebene über der Textebene, kein <mark> im Text. Warum, steht
|
||
* ausführlich in PdfViewer.tsx bei `paint` – kurz: Der Grund eines <mark>
|
||
* ragte oben aus der Textebene heraus, und die schneidet ab (`overflow:
|
||
* clip`). Das sah man als angeschnittene Streifen. Diese Ebene schneidet
|
||
* nichts ab; die Kästen sind dieselben, die der Browser beim Markieren mit
|
||
* der Maus malt.
|
||
*
|
||
* `pointer-events: none`, damit die Ebene das Markieren mit der Maus nicht
|
||
* abfängt – darunter liegt die Schrift, die man auswählen können soll.
|
||
*
|
||
* Die Farbe bleibt durchsichtig: Der lesbare Text steht auf dem gemalten Bild
|
||
* *darunter*. Mit 0,95 Deckkraft lag ein satter Block über dem Wort, und
|
||
* ausgerechnet das gesuchte Wort war das einzige, das man nicht mehr lesen
|
||
* konnte. Der Betrachter von PDF.js selbst arbeitet aus demselben Grund mit
|
||
* 0,25.
|
||
*/
|
||
.pdf-hitLayer {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 1;
|
||
pointer-events: none;
|
||
}
|
||
.pdf-hit {
|
||
position: absolute;
|
||
background: rgba(245, 158, 11, 0.3);
|
||
border-radius: 2px;
|
||
}
|
||
.pdf-hit.pdf-hit-current {
|
||
background: rgba(245, 158, 11, 0.55);
|
||
/* Der aktuelle Treffer bekommt zusätzlich eine Kante statt noch mehr Farbe –
|
||
Deckkraft würde wieder das Wort verschlucken. */
|
||
box-shadow: 0 0 0 1px rgba(180, 83, 9, 0.75);
|
||
}
|