projview 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +40 -0
- package/bin/projview.js +83 -0
- package/docs/README.md +28 -0
- package/docs/diagrams/architecture.mmd +9 -0
- package/docs/diagrams/broken-diagram.mmd +5 -0
- package/docs/diagrams/sequence.mmd +13 -0
- package/docs/edge-cases/deep/nested/buried.md +11 -0
- package/docs/edge-cases/faulty-refs.md +23 -0
- package/docs/edge-cases/syntax-stress.md +48 -0
- package/docs/getting-started.md +66 -0
- package/docs/guides/configuration.md +29 -0
- package/docs/guides/installation.md +22 -0
- package/docs/guides/troubleshooting.md +17 -0
- package/docs/reference/api.md +82 -0
- package/docs/reference/glossary.md +16 -0
- package/package.json +45 -0
- package/public/breadcrumb.js +39 -0
- package/public/dom.js +11 -0
- package/public/icons.js +7 -0
- package/public/index.html +71 -0
- package/public/links.js +31 -0
- package/public/live.js +31 -0
- package/public/main.js +27 -0
- package/public/mermaid.js +77 -0
- package/public/paths.js +14 -0
- package/public/search.js +163 -0
- package/public/store.js +8 -0
- package/public/style.css +551 -0
- package/public/toast.js +15 -0
- package/public/toc.js +51 -0
- package/public/tree.js +111 -0
- package/public/vendor/hljs.css +10 -0
- package/public/vendor/mermaid.min.js +3405 -0
- package/public/viewer.js +83 -0
- package/src/render.js +95 -0
- package/src/search.js +101 -0
- package/src/server.js +160 -0
- package/src/walk.js +57 -0
package/public/tree.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* left-nav file tree: render, expand/collapse, reveal */
|
|
2
|
+
|
|
3
|
+
import { $, esc, cssEsc } from './dom.js';
|
|
4
|
+
import { SVG_FOLDER, fileIcon } from './icons.js';
|
|
5
|
+
import { state } from './store.js';
|
|
6
|
+
import { openFile } from './viewer.js';
|
|
7
|
+
|
|
8
|
+
const treeEl = $('tree');
|
|
9
|
+
const fileCountEl = $('file-count');
|
|
10
|
+
const rootNameEl = $('root-name');
|
|
11
|
+
let fileCount = 0;
|
|
12
|
+
|
|
13
|
+
export async function loadTree() {
|
|
14
|
+
const res = await fetch('/api/tree');
|
|
15
|
+
const { root, tree } = await res.json();
|
|
16
|
+
state.treeData = tree;
|
|
17
|
+
state.rootName = root;
|
|
18
|
+
rootNameEl.textContent = root;
|
|
19
|
+
document.title = `${root} · projview`;
|
|
20
|
+
renderTree();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// (Re)build the tree DOM. Indentation comes from nesting children inside
|
|
24
|
+
// .children containers (which carry the guide line).
|
|
25
|
+
export function renderTree() {
|
|
26
|
+
fileCount = 0;
|
|
27
|
+
treeEl.replaceChildren(...state.treeData.map(renderNode));
|
|
28
|
+
fileCountEl.textContent = String(fileCount);
|
|
29
|
+
markActive(state.currentPath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function renderNode(node) {
|
|
33
|
+
if (node.type === 'dir') return renderDir(node, node.children.map(renderNode));
|
|
34
|
+
fileCount++;
|
|
35
|
+
return renderFileRow(node);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderDir(node, kidEls) {
|
|
39
|
+
const open = state.expanded.has(node.path);
|
|
40
|
+
const wrap = document.createElement('div');
|
|
41
|
+
const row = document.createElement('button');
|
|
42
|
+
row.className = 'row dir';
|
|
43
|
+
row.dataset.path = node.path;
|
|
44
|
+
row.innerHTML =
|
|
45
|
+
`<span class="twist">${open ? '▾' : '▸'}</span>` +
|
|
46
|
+
`<span class="ico">${SVG_FOLDER}</span>` +
|
|
47
|
+
`<span class="label">${esc(node.name)}</span>`;
|
|
48
|
+
|
|
49
|
+
const kids = document.createElement('div');
|
|
50
|
+
kids.className = 'children';
|
|
51
|
+
kids.hidden = !open;
|
|
52
|
+
for (const el of kidEls) kids.appendChild(el);
|
|
53
|
+
|
|
54
|
+
row.addEventListener('click', () => {
|
|
55
|
+
const nowOpen = kids.hidden;
|
|
56
|
+
kids.hidden = !nowOpen;
|
|
57
|
+
if (nowOpen) state.expanded.add(node.path); else state.expanded.delete(node.path);
|
|
58
|
+
row.querySelector('.twist').textContent = nowOpen ? '▾' : '▸';
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
wrap.appendChild(row);
|
|
62
|
+
wrap.appendChild(kids);
|
|
63
|
+
return wrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function renderFileRow(node) {
|
|
67
|
+
const row = document.createElement('button');
|
|
68
|
+
row.className = 'row file' + (node.path === state.currentPath ? ' active' : '');
|
|
69
|
+
row.dataset.path = node.path;
|
|
70
|
+
row.innerHTML =
|
|
71
|
+
`<span class="twist"></span>` +
|
|
72
|
+
`<span class="ico ico-${node.ext === '.mmd' ? 'mmd' : 'md'}">${fileIcon(node.ext)}</span>` +
|
|
73
|
+
`<span class="label">${esc(node.name)}</span>`;
|
|
74
|
+
row.addEventListener('click', () => openFile(node.path));
|
|
75
|
+
return row;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function markActive(p) {
|
|
79
|
+
for (const el of treeEl.querySelectorAll('.row.file.active')) el.classList.remove('active');
|
|
80
|
+
if (!p) return;
|
|
81
|
+
const el = treeEl.querySelector(`.row.file[data-path="${cssEsc(p)}"]`);
|
|
82
|
+
if (el) el.classList.add('active');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Expand every ancestor dir of a path. Returns true if anything changed.
|
|
86
|
+
function expandAncestors(p) {
|
|
87
|
+
const parts = p.split('/');
|
|
88
|
+
let changed = false;
|
|
89
|
+
for (let i = 1; i < parts.length; i++) {
|
|
90
|
+
const dir = parts.slice(0, i).join('/');
|
|
91
|
+
if (!state.expanded.has(dir)) { state.expanded.add(dir); changed = true; }
|
|
92
|
+
}
|
|
93
|
+
return changed;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Reveal a file in the nav: expand ancestors, re-mark active, scroll into view.
|
|
97
|
+
export function revealInTree(p) {
|
|
98
|
+
if (!p) return;
|
|
99
|
+
if (expandAncestors(p)) renderTree(); else markActive(p);
|
|
100
|
+
const el = treeEl.querySelector(`.row.file[data-path="${cssEsc(p)}"]`);
|
|
101
|
+
if (el) el.scrollIntoView({ block: 'nearest' });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Reveal a directory (from a breadcrumb click): expand it + ancestors, scroll to it.
|
|
105
|
+
export function revealDir(dirPath) {
|
|
106
|
+
expandAncestors(dirPath + '/x'); // expand dirPath and all its ancestors
|
|
107
|
+
state.expanded.add(dirPath);
|
|
108
|
+
renderTree();
|
|
109
|
+
const el = treeEl.querySelector(`.row.dir[data-path="${cssEsc(dirPath)}"]`);
|
|
110
|
+
if (el) el.scrollIntoView({ block: 'nearest' });
|
|
111
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
|
|
2
|
+
Theme: GitHub Dark
|
|
3
|
+
Description: Dark theme as seen on github.com
|
|
4
|
+
Author: github.com
|
|
5
|
+
Maintainer: @Hirse
|
|
6
|
+
Updated: 2021-05-15
|
|
7
|
+
|
|
8
|
+
Outdated base version: https://github.com/primer/github-syntax-dark
|
|
9
|
+
Current colors taken from GitHub's CSS
|
|
10
|
+
*/.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#79c0ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-code,.hljs-comment,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}
|