Skip to content

Commit f0fdd92

Browse files
committed
fix: improve pdf rendering and html
1 parent e54b78c commit f0fdd92

202 files changed

Lines changed: 999 additions & 2051 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

gulpfile.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ const coreJsFiles = [
2626
];
2727

2828
const vendorJsFiles = [
29-
"public/script/external/pdf.compat.js",
3029
"public/script/external/pdf.js",
31-
"public/script/ng-pdfviewer.min.js",
30+
"public/script/pdf-viewer.js",
31+
"public/script/html-doc.js",
3232
"public/script/external/katex.min.js",
3333
"public/script/external/katex-auto-render.min.js",
3434
"public/script/external/marked-katex-extension.umd.min.js",

public/asset-manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"core.min.js": "core.6332b3c288.min.js",
3-
"vendor.min.js": "vendor.c42de05f19.min.js",
3+
"vendor.min.js": "vendor.2c4b28d9af.min.js",
44
"mermaid.min.js": "mermaid.f848a72d16.min.js",
5-
"all.min.css": "all.be6d7c2c8b.min.css"
5+
"all.min.css": "all.b704319d9c.min.css"
66
}

public/css/all.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/css/style.css

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,11 @@ a:hover {
13291329
background: var(--hover-bg-color);
13301330
color: var(--color);
13311331
}
1332+
/* An action that is currently on, e.g. JS execution enabled for this file. */
1333+
.status-bar-actions .btn.btn-active {
1334+
border-color: currentColor;
1335+
font-weight: 600;
1336+
}
13321337

13331338
.paths {
13341339
flex: 1 1 auto;
@@ -1372,6 +1377,116 @@ a:hover {
13721377
padding: 4px;
13731378
}
13741379

1380+
/* Rendered PDF: a toolbar above a scrolling column of pages. */
1381+
.pdf-viewer {
1382+
display: flex;
1383+
flex-direction: column;
1384+
height: 100%;
1385+
min-height: 0;
1386+
background-color: var(--main-bg-color);
1387+
}
1388+
1389+
.pdf-toolbar {
1390+
display: flex;
1391+
align-items: center;
1392+
gap: 4px;
1393+
flex: 0 0 auto;
1394+
padding: 4px 8px;
1395+
border-bottom: 1px solid var(--border-color, rgba(0, 0, 0, 0.12));
1396+
}
1397+
1398+
.pdf-toolbar-group {
1399+
display: inline-flex;
1400+
align-items: center;
1401+
gap: 4px;
1402+
}
1403+
1404+
.pdf-toolbar-right {
1405+
margin-left: auto;
1406+
}
1407+
1408+
.pdf-toolbar-btn {
1409+
border: 0;
1410+
background: none;
1411+
color: inherit;
1412+
padding: 4px 8px;
1413+
border-radius: 4px;
1414+
font-size: 13px;
1415+
line-height: 1.2;
1416+
cursor: pointer;
1417+
}
1418+
1419+
.pdf-toolbar-btn:hover:not(:disabled) {
1420+
background-color: rgba(128, 128, 128, 0.18);
1421+
}
1422+
1423+
.pdf-toolbar-btn:disabled {
1424+
opacity: 0.4;
1425+
cursor: default;
1426+
}
1427+
1428+
.pdf-toolbar-zoom {
1429+
min-width: 52px;
1430+
font-variant-numeric: tabular-nums;
1431+
}
1432+
1433+
.pdf-toolbar-page {
1434+
width: 3.5em;
1435+
text-align: right;
1436+
padding: 2px 4px;
1437+
font-size: 13px;
1438+
color: inherit;
1439+
background: none;
1440+
border: 1px solid var(--border-color, rgba(0, 0, 0, 0.2));
1441+
border-radius: 4px;
1442+
}
1443+
1444+
.pdf-toolbar-count {
1445+
font-size: 13px;
1446+
opacity: 0.75;
1447+
font-variant-numeric: tabular-nums;
1448+
}
1449+
1450+
.pdf-pages {
1451+
flex: 1 1 auto;
1452+
min-height: 0;
1453+
overflow: auto;
1454+
padding: 8px 0;
1455+
}
1456+
1457+
.pdf-viewer-page {
1458+
/* Sized to the page's aspect ratio while empty so the scrollbar is honest
1459+
and only on-screen pages get rasterised; the canvas takes over once the
1460+
page renders. */
1461+
margin: 0 auto 12px;
1462+
}
1463+
1464+
.pdf-viewer-page canvas {
1465+
display: block;
1466+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
1467+
}
1468+
1469+
.pdf-viewer-error {
1470+
padding: 24px;
1471+
text-align: center;
1472+
font-weight: bold;
1473+
}
1474+
1475+
/* Rendered HTML document. Always light: the document brings its own styling
1476+
and dark mode can't reach inside a cross-origin frame to adapt it. */
1477+
.html-doc {
1478+
display: block;
1479+
height: 100%;
1480+
}
1481+
1482+
.html-doc-frame {
1483+
display: block;
1484+
width: 100%;
1485+
height: 100%;
1486+
border: 0;
1487+
background-color: #fff;
1488+
}
1489+
13751490
.file-error {
13761491
font-weight: bold;
13771492
font-size: 50px;

public/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
</script>
106106
<script src="/script/__VENDOR_JS__" defer onload="
107107
ace.config.set('basePath', '/script/external/ace/');
108-
PDFJS.workerSrc = '/script/external/pdf.worker.js';
108+
pdfjsLib.GlobalWorkerOptions.workerSrc = '/script/external/pdf.worker.js';
109109
angular.bootstrap(document, ['anonymous-github']);
110110
"></script>
111111
<script>

public/partials/explorer.htm

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,30 @@
9191
aria-label="Edit"
9292
><i class="far fa-edit"></i><span class="d-none d-md-inline"> Edit</span></a
9393
>
94+
<button
95+
ng-if="type == 'html-doc' && !showSource"
96+
ng-click="toggleAllowScripts()"
97+
class="btn btn-sm"
98+
ng-class="{'btn-active': allowScripts}"
99+
aria-label="Allow this document to run JavaScript"
100+
title="Scripts in this file are blocked by default. Enable them only if you trust the repository — the document stays isolated from your session either way."
101+
>
102+
<i class="fab fa-js"></i><span class="d-none d-md-inline">
103+
{{ allowScripts ? "JS on" : "JS off" }}</span
104+
>
105+
</button>
106+
<button
107+
ng-if="type == 'html-doc'"
108+
ng-click="toggleSource()"
109+
class="btn btn-sm"
110+
aria-label="Toggle between the rendered document and its source"
111+
title="Toggle between the rendered document and its source"
112+
>
113+
<i class="fas" ng-class="showSource ? 'fa-eye' : 'fa-code'"></i
114+
><span class="d-none d-md-inline">
115+
{{ showSource ? "Rendered" : "Source" }}</span
116+
>
117+
</button>
94118
<a
95119
ng-show="content != null"
96120
ng-href="{{url}}"

public/partials/pageView.htm

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
<div ng-if="type == 'text'" ng-model="content" ui-ace="aceOption"></div>
22
<div ng-if="type == 'html'" ng-bind-html="content" class="file-content markdown-body"></div>
3-
<div ng-if="type == 'code' && content != null" ui-ace="aceOption" ng-model="content"></div>
3+
<html-doc
4+
ng-if="type == 'html-doc' && !showSource && content != null"
5+
content="content"
6+
base-url="{{fileBaseUrl}}"
7+
allow-scripts="allowScripts"
8+
></html-doc>
9+
<div
10+
ng-if="(type == 'code' || (type == 'html-doc' && showSource)) && content != null"
11+
ui-ace="aceOption"
12+
ng-model="content"
13+
></div>
414
<img ng-if="type == 'image'" class="image-content" ng-src="{{url}}"></img>
515
<iframe class="h-100 overflow-auto w-100 b-0" ng-if="type == 'media'" ng-src="{{url}}"></iframe>
6-
<div class="h-100 overflow-auto" ng-if="type == 'pdf'">
7-
<pdfviewer class="h-100 overflow-auto" src="{{url}}" id="viewer"></pdfviewer>
8-
</div>
16+
<pdfviewer ng-if="type == 'pdf'" src="{{url}}"></pdfviewer>
917
<div ng-if="type == 'audio'"><audio controls="controls"><source ng-src="{{url}}" /></audio></div>
1018
<div ng-if="type == 'IPython'"><notebook file="url"></notebook></div>
1119
<div ng-if="type == 'rate_limited'" class="file-error container d-flex h-100">

public/script/app.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ angular
44
"ngSanitize",
55
"ui.ace",
66
"ngPDFViewer",
7+
"htmlDoc",
78
"pascalprecht.translate",
89
"admin",
910
])
@@ -2559,8 +2560,7 @@ angular
25592560
"$routeParams",
25602561
"$sce",
25612562
"$q",
2562-
"PDFViewerService",
2563-
function ($scope, $http, $location, $routeParams, $sce, $q, PDFViewerService) {
2563+
function ($scope, $http, $location, $routeParams, $sce, $q) {
25642564
$scope.files = [];
25652565
$scope.isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent);
25662566
$scope.fileSearchQuery = "";
@@ -2843,6 +2843,18 @@ angular
28432843
);
28442844
}
28452845

2846+
// Defined as methods rather than inline `showSource = !showSource` in the
2847+
// template: the toolbar and the file view sit in different child scopes,
2848+
// so an inline assignment would shadow the value on one of them instead
2849+
// of updating the controller's.
2850+
$scope.toggleSource = function () {
2851+
$scope.showSource = !$scope.showSource;
2852+
};
2853+
2854+
$scope.toggleAllowScripts = function () {
2855+
$scope.allowScripts = !$scope.allowScripts;
2856+
};
2857+
28462858
function getMode(extension) {
28472859
if (extensionModes[extension]) {
28482860
return extensionModes[extension];
@@ -2852,9 +2864,14 @@ angular
28522864

28532865
function getType(extension) {
28542866
if (extension == "pdf") {
2855-
$scope.instance = PDFViewerService.Instance("viewer");
28562867
return "pdf";
28572868
}
2869+
// Rendered as a document in a sandboxed frame rather than as source —
2870+
// see html-doc.js. "html" is reserved for markup we generated
2871+
// ourselves (rendered markdown/org) and inject directly.
2872+
if (extension == "html" || extension == "htm") {
2873+
return "html-doc";
2874+
}
28582875
if (extension == "md") {
28592876
return "md";
28602877
}
@@ -2975,6 +2992,20 @@ angular
29752992
$scope.url = `/api/repo/${$scope.repoId}/file/${encodePathForUrl(
29762993
$scope.filePath
29772994
)}?v=${fileVersion}`;
2995+
// Directory the file lives in, used as the <base> for a rendered HTML
2996+
// document so its relative images/stylesheets still resolve.
2997+
const dirPath = $scope.filePath.substring(
2998+
0,
2999+
$scope.filePath.lastIndexOf("/") + 1
3000+
);
3001+
$scope.fileBaseUrl = `/api/repo/${$scope.repoId}/file/${
3002+
dirPath ? encodePathForUrl(dirPath) : ""
3003+
}`;
3004+
$scope.showSource = false;
3005+
// Scripts in a repository's HTML are opt-in, per file — see
3006+
// html-doc.js. Reset on navigation so trust never carries over from
3007+
// one file to the next.
3008+
$scope.allowScripts = false;
29783009

29793010
let extension = $scope.filePath.toLowerCase();
29803011
const extensionIndex = extension.lastIndexOf(".");
@@ -3112,6 +3143,15 @@ angular
31123143
}
31133144
$scope.type = getType(extension);
31143145

3146+
if ($scope.type == "pdf") {
3147+
// The viewer streams the file itself from $scope.url, so fetching
3148+
// the bytes again here only to hold them as a JS string wastes a
3149+
// request and a lot of memory on a large report. Content stays
3150+
// non-null so the Raw/Download actions remain available.
3151+
$scope.content = "pdf";
3152+
return;
3153+
}
3154+
31153155
getContent($scope.filePath, $scope.file);
31163156
}
31173157

2.35 KB
Binary file not shown.
173 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)