ework-web 0.10.38 → 0.10.40
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/package.json +1 -1
- package/src/remote-file.ts +93 -13
package/package.json
CHANGED
package/src/remote-file.ts
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import { THEME_CSS, escapeHtml, tabNavHTML } from "./render/layout";
|
|
2
|
+
import hljs from "highlight.js";
|
|
3
|
+
import { renderMarkdown } from "./render/markdown";
|
|
4
|
+
|
|
5
|
+
// Extension → hljs language map (mirrors fileview.ts for parity).
|
|
6
|
+
const EXT_LANG: Record<string, string> = {
|
|
7
|
+
js: "javascript", mjs: "javascript", cjs: "javascript", jsx: "javascript",
|
|
8
|
+
ts: "typescript", tsx: "typescript", mts: "typescript", cts: "typescript",
|
|
9
|
+
py: "python", rb: "ruby", go: "go", rs: "rust", java: "java", kt: "kotlin",
|
|
10
|
+
c: "c", h: "c", cpp: "cpp", cc: "cpp", cxx: "cpp", hpp: "cpp", hxx: "cpp",
|
|
11
|
+
cs: "csharp", php: "php", swift: "swift", scala: "scala", sh: "bash",
|
|
12
|
+
bash: "bash", zsh: "bash", fish: "bash", yml: "yaml", yaml: "yaml",
|
|
13
|
+
json: "json", json5: "json", toml: "toml", xml: "xml", html: "xml",
|
|
14
|
+
htm: "xml", css: "css", scss: "scss", less: "less", sql: "sql",
|
|
15
|
+
dockerfile: "dockerfile", makefile: "makefile", lua: "lua", pl: "perl",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function extToLang(p: string): string {
|
|
19
|
+
const base = (p.split("/").pop() || "").toLowerCase();
|
|
20
|
+
if (base === "dockerfile" || base.endsWith(".dockerfile")) return "dockerfile";
|
|
21
|
+
if (base === "makefile" || base.endsWith(".mk")) return "makefile";
|
|
22
|
+
const m = base.match(/\.([a-z0-9]+)$/);
|
|
23
|
+
const ext = m && m[1];
|
|
24
|
+
return ext ? (EXT_LANG[ext] || "") : "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hljsHighlight(text: string, lang: string): string {
|
|
28
|
+
try {
|
|
29
|
+
return hljs.highlight(text, { language: lang }).value;
|
|
30
|
+
} catch {
|
|
31
|
+
return escapeHtml(text);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
2
34
|
|
|
3
35
|
export class RemoteFileError extends Error {
|
|
4
36
|
status: number;
|
|
@@ -86,7 +118,7 @@ export async function browseRemoteFile(
|
|
|
86
118
|
throw new RemoteFileError(err.error ?? `daemon returned ${readResp.status}`, readResp.status);
|
|
87
119
|
}
|
|
88
120
|
const data = (await readResp.json()) as FileContent;
|
|
89
|
-
return { html: renderFileContent(data, endpoint, daemonLabel, viewerLogin) };
|
|
121
|
+
return { html: renderFileContent(data, rawPath, endpoint, daemonLabel, viewerLogin) };
|
|
90
122
|
}
|
|
91
123
|
|
|
92
124
|
function renderDirListing(
|
|
@@ -147,6 +179,7 @@ ${tabNavHTML("sessions", user)}
|
|
|
147
179
|
|
|
148
180
|
function renderFileContent(
|
|
149
181
|
data: FileContent,
|
|
182
|
+
rawPath: string,
|
|
150
183
|
endpoint: string,
|
|
151
184
|
daemonLabel: string,
|
|
152
185
|
viewerLogin?: string
|
|
@@ -154,31 +187,78 @@ function renderFileContent(
|
|
|
154
187
|
const enc = encodeURIComponent;
|
|
155
188
|
const daemonParam = `&daemon=${enc(endpoint)}`;
|
|
156
189
|
const user = viewerLogin ? { login: viewerLogin, is_admin: 0 } : undefined;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
190
|
+
|
|
191
|
+
const ext = (rawPath.split(".").pop() || "").toLowerCase();
|
|
192
|
+
const isMd = ext === "md" || ext === "markdown";
|
|
193
|
+
const lang = extToLang(rawPath);
|
|
194
|
+
const fullText = data.rows.map((r) => r.t).join("\n");
|
|
195
|
+
const shownBytes = data.rows.reduce((s, r) => s + r.t.length + 1, 0);
|
|
196
|
+
|
|
197
|
+
let body: string;
|
|
198
|
+
if (isMd) {
|
|
199
|
+
body = `<div class="md-render">${renderMarkdown(fullText, "")}</div>`;
|
|
200
|
+
} else if (lang && shownBytes <= 256000) {
|
|
201
|
+
const nums = data.rows.map((r) => r.n).join("\n");
|
|
202
|
+
body = `<div class="fv-code"><pre class="fv-gutter">${escapeHtml(nums)}</pre><pre class="fv-src"><code class="hljs language-${escapeHtml(lang)}">${hljsHighlight(fullText, lang)}</code></pre></div>`;
|
|
203
|
+
} else {
|
|
204
|
+
body = `<pre class="fv-plain"><code>${data.rows.map((r) => `<span class="ln"><span class="lnn">${r.n}</span><span class="lnc">${escapeHtml(r.t)}</span></span>`).join("")}</code></pre>`;
|
|
205
|
+
}
|
|
160
206
|
|
|
161
207
|
return `<!doctype html>
|
|
162
208
|
<html lang="zh"><head><meta charset="utf-8">
|
|
163
209
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
164
210
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
165
211
|
<title>ework-web · ${escapeHtml(data.path)}</title>
|
|
212
|
+
<link rel="stylesheet" href="/static/highlight.css">
|
|
166
213
|
<style>${THEME_CSS}
|
|
214
|
+
.hljs{color:var(--text);background:transparent}
|
|
215
|
+
.hljs-comment,.hljs-quote{color:var(--text-muted);font-style:italic}
|
|
216
|
+
.hljs-keyword,.hljs-selector-tag,.hljs-built_in,.hljs-type{color:#d73a49;font-weight:600}
|
|
217
|
+
.hljs-string,.hljs-attr,.hljs-template-tag,.hljs-addition{color:#032f62}
|
|
218
|
+
.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable{color:#005cc5}
|
|
219
|
+
.hljs-title,.hljs-section,.hljs-name,.hljs-function .hljs-title{color:#6f42c1;font-weight:600}
|
|
220
|
+
.hljs-tag{color:var(--text-muted)}
|
|
221
|
+
.hljs-attribute{color:#005cc5}
|
|
222
|
+
.hljs-regexp,.hljs-link{color:#032f62}
|
|
223
|
+
.hljs-deletion{color:#b31d28;background-color:#ffeef0}
|
|
224
|
+
.hljs-addition{background-color:#f0fff4}
|
|
225
|
+
@media(prefers-color-scheme:dark){.hljs-keyword,.hljs-selector-tag,.hljs-built_in,.hljs-type{color:#ff7b72}.hljs-string,.hljs-attr,.hljs-template-tag{color:#a5d6ff}.hljs-number,.hljs-literal,.hljs-variable{color:#79c0ff}.hljs-title,.hljs-section,.hljs-name,.hljs-function .hljs-title{color:#d2a8ff}.hljs-comment,.hljs-quote{color:#8b949e}.hljs-deletion{color:#ffa198;background:#67060c}.hljs-addition{background:#033a16}}
|
|
167
226
|
.nav{display:flex;align-items:center;gap:.5rem;padding:.55rem 1rem;background:var(--header-bg);color:var(--header-text);font-size:13px}
|
|
168
227
|
.nav a{color:var(--header-text);opacity:.95}
|
|
169
|
-
.wrap{max-width:
|
|
228
|
+
.wrap{max-width:1100px;margin:0 auto;padding:1rem}
|
|
170
229
|
h1{font-size:14px;margin:0 0 .3rem;font-family:ui-monospace,monospace;word-break:break-all}
|
|
171
230
|
.remote-badge{display:inline-block;background:#1f6feb33;color:#58a6ff;padding:2px 8px;border-radius:10px;font-size:11px;font-weight:600;margin-left:.5rem}
|
|
172
231
|
.note{color:var(--text-muted);font-size:12px;margin:0 0 .5rem}
|
|
173
232
|
.parent-link{font-size:13px;margin-bottom:.4rem}
|
|
174
233
|
.parent-link a{color:var(--text-muted)}
|
|
175
|
-
.
|
|
176
|
-
.
|
|
177
|
-
.
|
|
178
|
-
.
|
|
179
|
-
.
|
|
180
|
-
.
|
|
181
|
-
.
|
|
234
|
+
.fv-code{display:flex;border:1px solid var(--border);border-radius:8px;overflow:auto;font-size:12.5px;line-height:1.5}
|
|
235
|
+
.fv-gutter{background:var(--bg-muted);color:var(--text-muted);text-align:right;padding:.6rem .7rem;user-select:none;white-space:pre;flex-shrink:0;border:none;margin:0}
|
|
236
|
+
.fv-src{margin:0;flex:1;overflow:auto;background:var(--bg-elev);padding:.6rem .8rem;border:none}
|
|
237
|
+
.fv-src code{background:none;padding:0;white-space:pre;font-size:12.5px}
|
|
238
|
+
.fv-plain{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:.6rem 0;overflow:auto;font-size:12.5px;line-height:1.5}
|
|
239
|
+
.fv-plain .ln{display:flex}
|
|
240
|
+
.fv-plain .lnn{user-select:none;text-align:right;color:var(--text-muted);padding:0 .7rem 0 .8rem;min-width:3.5em;flex-shrink:0;border-right:1px solid var(--border);background:var(--bg-muted)}
|
|
241
|
+
.fv-plain .lnc{padding:0 .8rem;white-space:pre;overflow-wrap:anywhere}
|
|
242
|
+
.md-render{max-width:820px;margin:0 auto;padding:1rem 1.5rem 5rem;font-size:14.5px;line-height:1.7;color:var(--text)}
|
|
243
|
+
.md-render h1,.md-render h2,.md-render h3,.md-render h4{line-height:1.3;margin:1.4em 0 .6em;font-weight:600}
|
|
244
|
+
.md-render h1{font-size:1.7em;border-bottom:1px solid var(--border);padding-bottom:.3em}
|
|
245
|
+
.md-render h2{font-size:1.4em;border-bottom:1px solid var(--border);padding-bottom:.3em}
|
|
246
|
+
.md-render h3{font-size:1.2em}
|
|
247
|
+
.md-render h4{font-size:1.05em}
|
|
248
|
+
.md-render p{margin:.6em 0}
|
|
249
|
+
.md-render ul,.md-render ol{margin:.6em 0;padding-left:1.6em}
|
|
250
|
+
.md-render li{margin:.25em 0}
|
|
251
|
+
.md-render a{color:var(--accent);text-decoration:none}
|
|
252
|
+
.md-render a:hover{text-decoration:underline}
|
|
253
|
+
.md-render code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.88em;background:var(--bg-muted);padding:.15em .35em;border-radius:4px}
|
|
254
|
+
.md-render pre{background:var(--bg-elev);border:1px solid var(--border);border-radius:8px;padding:.7rem 1rem;overflow:auto;margin:.8em 0}
|
|
255
|
+
.md-render pre code{background:none;padding:0;font-size:13px}
|
|
256
|
+
.md-render blockquote{border-left:3px solid var(--border);margin:.8em 0;padding:.2em 0 .2em 1em;color:var(--text-muted)}
|
|
257
|
+
.md-render table{border-collapse:collapse;margin:.8em 0;width:100%}
|
|
258
|
+
.md-render th,.md-render td{border:1px solid var(--border);padding:.4em .7em;text-align:left}
|
|
259
|
+
.md-render th{background:var(--bg-muted);font-weight:600}
|
|
260
|
+
.md-render img{max-width:100%}
|
|
261
|
+
.md-render hr{border:none;border-top:1px solid var(--border);margin:1.4em 0}
|
|
182
262
|
</style></head><body>
|
|
183
263
|
<header class="nav"><a href="/" style="color:var(--header-text)">🏠 ework-web</a></header>
|
|
184
264
|
${tabNavHTML("sessions", user)}
|
|
@@ -186,7 +266,7 @@ ${tabNavHTML("sessions", user)}
|
|
|
186
266
|
<div class="parent-link">📁 <a href="/file?path=${enc(data.path.split("/").slice(0, -1).join("/") || "/")}${daemonParam}">..</a></div>
|
|
187
267
|
<h1>${escapeHtml(data.path)}<span class="remote-badge">远程 ${daemonLabel}</span></h1>
|
|
188
268
|
${data.note ? `<p class="note">${escapeHtml(data.note)}</p>` : ""}
|
|
189
|
-
|
|
269
|
+
${body}
|
|
190
270
|
</main></body></html>`;
|
|
191
271
|
}
|
|
192
272
|
|