@wendongfly/myhi 1.0.1

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.
@@ -0,0 +1,431 @@
1
+ "use strict";
2
+ var __makeTemplateObject = (this && this.__makeTemplateObject) || function (cooked, raw) {
3
+ if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; }
4
+ return cooked;
5
+ };
6
+ var PacketKind;
7
+ (function (PacketKind) {
8
+ PacketKind[PacketKind["EOS"] = 0] = "EOS";
9
+ PacketKind[PacketKind["Text"] = 1] = "Text";
10
+ PacketKind[PacketKind["Incomplete"] = 2] = "Incomplete";
11
+ PacketKind[PacketKind["ESC"] = 3] = "ESC";
12
+ PacketKind[PacketKind["Unknown"] = 4] = "Unknown";
13
+ PacketKind[PacketKind["SGR"] = 5] = "SGR";
14
+ PacketKind[PacketKind["OSCURL"] = 6] = "OSCURL";
15
+ })(PacketKind || (PacketKind = {}));
16
+ export class AnsiUp {
17
+ constructor() {
18
+ this.VERSION = "6.0.6";
19
+ this.setup_palettes();
20
+ this._use_classes = false;
21
+ this.bold = false;
22
+ this.faint = false;
23
+ this.italic = false;
24
+ this.underline = false;
25
+ this.fg = this.bg = null;
26
+ this._buffer = '';
27
+ this._url_allowlist = { 'http': 1, 'https': 1 };
28
+ this._escape_html = true;
29
+ this.boldStyle = 'font-weight:bold';
30
+ this.faintStyle = 'opacity:0.7';
31
+ this.italicStyle = 'font-style:italic';
32
+ this.underlineStyle = 'text-decoration:underline';
33
+ }
34
+ set use_classes(arg) {
35
+ this._use_classes = arg;
36
+ }
37
+ get use_classes() {
38
+ return this._use_classes;
39
+ }
40
+ set url_allowlist(arg) {
41
+ this._url_allowlist = arg;
42
+ }
43
+ get url_allowlist() {
44
+ return this._url_allowlist;
45
+ }
46
+ set escape_html(arg) {
47
+ this._escape_html = arg;
48
+ }
49
+ get escape_html() {
50
+ return this._escape_html;
51
+ }
52
+ set boldStyle(arg) { this._boldStyle = arg; }
53
+ get boldStyle() { return this._boldStyle; }
54
+ set faintStyle(arg) { this._faintStyle = arg; }
55
+ get faintStyle() { return this._faintStyle; }
56
+ set italicStyle(arg) { this._italicStyle = arg; }
57
+ get italicStyle() { return this._italicStyle; }
58
+ set underlineStyle(arg) { this._underlineStyle = arg; }
59
+ get underlineStyle() { return this._underlineStyle; }
60
+ setup_palettes() {
61
+ this.ansi_colors =
62
+ [
63
+ [
64
+ { rgb: [0, 0, 0], class_name: "ansi-black" },
65
+ { rgb: [187, 0, 0], class_name: "ansi-red" },
66
+ { rgb: [0, 187, 0], class_name: "ansi-green" },
67
+ { rgb: [187, 187, 0], class_name: "ansi-yellow" },
68
+ { rgb: [0, 0, 187], class_name: "ansi-blue" },
69
+ { rgb: [187, 0, 187], class_name: "ansi-magenta" },
70
+ { rgb: [0, 187, 187], class_name: "ansi-cyan" },
71
+ { rgb: [255, 255, 255], class_name: "ansi-white" }
72
+ ],
73
+ [
74
+ { rgb: [85, 85, 85], class_name: "ansi-bright-black" },
75
+ { rgb: [255, 85, 85], class_name: "ansi-bright-red" },
76
+ { rgb: [0, 255, 0], class_name: "ansi-bright-green" },
77
+ { rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
78
+ { rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
79
+ { rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
80
+ { rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
81
+ { rgb: [255, 255, 255], class_name: "ansi-bright-white" }
82
+ ]
83
+ ];
84
+ this.palette_256 = [];
85
+ this.ansi_colors.forEach(palette => {
86
+ palette.forEach(rec => {
87
+ this.palette_256.push(rec);
88
+ });
89
+ });
90
+ let levels = [0, 95, 135, 175, 215, 255];
91
+ for (let r = 0; r < 6; ++r) {
92
+ for (let g = 0; g < 6; ++g) {
93
+ for (let b = 0; b < 6; ++b) {
94
+ let col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
95
+ this.palette_256.push(col);
96
+ }
97
+ }
98
+ }
99
+ let grey_level = 8;
100
+ for (let i = 0; i < 24; ++i, grey_level += 10) {
101
+ let gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
102
+ this.palette_256.push(gry);
103
+ }
104
+ }
105
+ escape_txt_for_html(txt) {
106
+ if (!this._escape_html)
107
+ return txt;
108
+ return txt.replace(/[&<>"']/gm, (str) => {
109
+ if (str === "&")
110
+ return "&amp;";
111
+ if (str === "<")
112
+ return "&lt;";
113
+ if (str === ">")
114
+ return "&gt;";
115
+ if (str === "\"")
116
+ return "&quot;";
117
+ if (str === "'")
118
+ return "&#x27;";
119
+ });
120
+ }
121
+ append_buffer(txt) {
122
+ var str = this._buffer + txt;
123
+ this._buffer = str;
124
+ }
125
+ get_next_packet() {
126
+ var pkt = {
127
+ kind: PacketKind.EOS,
128
+ text: '',
129
+ url: ''
130
+ };
131
+ var len = this._buffer.length;
132
+ if (len == 0)
133
+ return pkt;
134
+ var pos = this._buffer.indexOf("\x1B");
135
+ if (pos == -1) {
136
+ pkt.kind = PacketKind.Text;
137
+ pkt.text = this._buffer;
138
+ this._buffer = '';
139
+ return pkt;
140
+ }
141
+ if (pos > 0) {
142
+ pkt.kind = PacketKind.Text;
143
+ pkt.text = this._buffer.slice(0, pos);
144
+ this._buffer = this._buffer.slice(pos);
145
+ return pkt;
146
+ }
147
+ if (pos == 0) {
148
+ if (len < 3) {
149
+ pkt.kind = PacketKind.Incomplete;
150
+ return pkt;
151
+ }
152
+ var next_char = this._buffer.charAt(1);
153
+ if ((next_char != '[') && (next_char != ']') && (next_char != '(')) {
154
+ pkt.kind = PacketKind.ESC;
155
+ pkt.text = this._buffer.slice(0, 1);
156
+ this._buffer = this._buffer.slice(1);
157
+ return pkt;
158
+ }
159
+ if (next_char == '[') {
160
+ if (!this._csi_regex) {
161
+ this._csi_regex = rgx(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \u001B[ # CSI\n ([<-?]?) # private-mode char\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \u001B[ # CSI\n [ -~]* # anything legal\n ([\0-\u001F:]) # anything illegal\n )\n "], ["\n ^ # beginning of line\n #\n # First attempt\n (?: # legal sequence\n \\x1b\\[ # CSI\n ([\\x3c-\\x3f]?) # private-mode char\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n )\n | # alternate (second attempt)\n (?: # illegal sequence\n \\x1b\\[ # CSI\n [\\x20-\\x7e]* # anything legal\n ([\\x00-\\x1f:]) # anything illegal\n )\n "])));
162
+ }
163
+ let match = this._buffer.match(this._csi_regex);
164
+ if (match === null) {
165
+ pkt.kind = PacketKind.Incomplete;
166
+ return pkt;
167
+ }
168
+ if (match[4]) {
169
+ pkt.kind = PacketKind.ESC;
170
+ pkt.text = this._buffer.slice(0, 1);
171
+ this._buffer = this._buffer.slice(1);
172
+ return pkt;
173
+ }
174
+ if ((match[1] != '') || (match[3] != 'm'))
175
+ pkt.kind = PacketKind.Unknown;
176
+ else
177
+ pkt.kind = PacketKind.SGR;
178
+ pkt.text = match[2];
179
+ var rpos = match[0].length;
180
+ this._buffer = this._buffer.slice(rpos);
181
+ return pkt;
182
+ }
183
+ else if (next_char == ']') {
184
+ if (len < 4) {
185
+ pkt.kind = PacketKind.Incomplete;
186
+ return pkt;
187
+ }
188
+ if ((this._buffer.charAt(2) != '8')
189
+ || (this._buffer.charAt(3) != ';')) {
190
+ pkt.kind = PacketKind.ESC;
191
+ pkt.text = this._buffer.slice(0, 1);
192
+ this._buffer = this._buffer.slice(1);
193
+ return pkt;
194
+ }
195
+ if (!this._osc_st) {
196
+ this._osc_st = rgxG(templateObject_2 || (templateObject_2 = __makeTemplateObject(["\n (?: # legal sequence\n (\u001B\\) # ESC | # alternate\n (\u0007) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\0-\u0006] # anything illegal\n | # alternate\n [\b-\u001A] # anything illegal\n | # alternate\n [\u001C-\u001F] # anything illegal\n )\n "], ["\n (?: # legal sequence\n (\\x1b\\\\) # ESC \\\n | # alternate\n (\\x07) # BEL (what xterm did)\n )\n | # alternate (second attempt)\n ( # illegal sequence\n [\\x00-\\x06] # anything illegal\n | # alternate\n [\\x08-\\x1a] # anything illegal\n | # alternate\n [\\x1c-\\x1f] # anything illegal\n )\n "])));
197
+ }
198
+ this._osc_st.lastIndex = 0;
199
+ {
200
+ let match = this._osc_st.exec(this._buffer);
201
+ if (match === null) {
202
+ pkt.kind = PacketKind.Incomplete;
203
+ return pkt;
204
+ }
205
+ if (match[3]) {
206
+ pkt.kind = PacketKind.ESC;
207
+ pkt.text = this._buffer.slice(0, 1);
208
+ this._buffer = this._buffer.slice(1);
209
+ return pkt;
210
+ }
211
+ }
212
+ {
213
+ let match = this._osc_st.exec(this._buffer);
214
+ if (match === null) {
215
+ pkt.kind = PacketKind.Incomplete;
216
+ return pkt;
217
+ }
218
+ if (match[3]) {
219
+ pkt.kind = PacketKind.ESC;
220
+ pkt.text = this._buffer.slice(0, 1);
221
+ this._buffer = this._buffer.slice(1);
222
+ return pkt;
223
+ }
224
+ }
225
+ if (!this._osc_regex) {
226
+ this._osc_regex = rgx(templateObject_3 || (templateObject_3 = __makeTemplateObject(["\n ^ # beginning of line\n #\n \u001B]8; # OSC Hyperlink\n [ -:<-~]* # params (excluding ;)\n ; # end of params\n ([!-~]{0,512}) # URL capture\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n ([ -~]+) # TEXT capture\n \u001B]8;; # OSC Hyperlink End\n (?: # ST\n (?:\u001B\\) # ESC | # alternate\n (?:\u0007) # BEL (what xterm did)\n )\n "], ["\n ^ # beginning of line\n #\n \\x1b\\]8; # OSC Hyperlink\n [\\x20-\\x3a\\x3c-\\x7e]* # params (excluding ;)\n ; # end of params\n ([\\x21-\\x7e]{0,512}) # URL capture\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n ([\\x20-\\x7e]+) # TEXT capture\n \\x1b\\]8;; # OSC Hyperlink End\n (?: # ST\n (?:\\x1b\\\\) # ESC \\\n | # alternate\n (?:\\x07) # BEL (what xterm did)\n )\n "])));
227
+ }
228
+ let match = this._buffer.match(this._osc_regex);
229
+ if (match === null) {
230
+ pkt.kind = PacketKind.ESC;
231
+ pkt.text = this._buffer.slice(0, 1);
232
+ this._buffer = this._buffer.slice(1);
233
+ return pkt;
234
+ }
235
+ pkt.kind = PacketKind.OSCURL;
236
+ pkt.url = match[1];
237
+ pkt.text = match[2];
238
+ var rpos = match[0].length;
239
+ this._buffer = this._buffer.slice(rpos);
240
+ return pkt;
241
+ }
242
+ else if (next_char == '(') {
243
+ pkt.kind = PacketKind.Unknown;
244
+ this._buffer = this._buffer.slice(3);
245
+ return pkt;
246
+ }
247
+ }
248
+ }
249
+ ansi_to_html(txt) {
250
+ this.append_buffer(txt);
251
+ var blocks = [];
252
+ while (true) {
253
+ var packet = this.get_next_packet();
254
+ if ((packet.kind == PacketKind.EOS)
255
+ || (packet.kind == PacketKind.Incomplete))
256
+ break;
257
+ if ((packet.kind == PacketKind.ESC)
258
+ || (packet.kind == PacketKind.Unknown))
259
+ continue;
260
+ if (packet.kind == PacketKind.Text)
261
+ blocks.push(this.transform_to_html(this.with_state(packet)));
262
+ else if (packet.kind == PacketKind.SGR)
263
+ this.process_ansi(packet);
264
+ else if (packet.kind == PacketKind.OSCURL)
265
+ blocks.push(this.process_hyperlink(packet));
266
+ }
267
+ return blocks.join("");
268
+ }
269
+ with_state(pkt) {
270
+ return { bold: this.bold, faint: this.faint, italic: this.italic, underline: this.underline, fg: this.fg, bg: this.bg, text: pkt.text };
271
+ }
272
+ process_ansi(pkt) {
273
+ let sgr_cmds = pkt.text.split(';');
274
+ while (sgr_cmds.length > 0) {
275
+ let sgr_cmd_str = sgr_cmds.shift();
276
+ let num = parseInt(sgr_cmd_str, 10);
277
+ if (isNaN(num) || num === 0) {
278
+ this.fg = null;
279
+ this.bg = null;
280
+ this.bold = false;
281
+ this.faint = false;
282
+ this.italic = false;
283
+ this.underline = false;
284
+ }
285
+ else if (num === 1) {
286
+ this.bold = true;
287
+ }
288
+ else if (num === 2) {
289
+ this.faint = true;
290
+ }
291
+ else if (num === 3) {
292
+ this.italic = true;
293
+ }
294
+ else if (num === 4) {
295
+ this.underline = true;
296
+ }
297
+ else if (num === 21) {
298
+ this.bold = false;
299
+ }
300
+ else if (num === 22) {
301
+ this.faint = false;
302
+ this.bold = false;
303
+ }
304
+ else if (num === 23) {
305
+ this.italic = false;
306
+ }
307
+ else if (num === 24) {
308
+ this.underline = false;
309
+ }
310
+ else if (num === 39) {
311
+ this.fg = null;
312
+ }
313
+ else if (num === 49) {
314
+ this.bg = null;
315
+ }
316
+ else if ((num >= 30) && (num < 38)) {
317
+ this.fg = this.ansi_colors[0][(num - 30)];
318
+ }
319
+ else if ((num >= 40) && (num < 48)) {
320
+ this.bg = this.ansi_colors[0][(num - 40)];
321
+ }
322
+ else if ((num >= 90) && (num < 98)) {
323
+ this.fg = this.ansi_colors[1][(num - 90)];
324
+ }
325
+ else if ((num >= 100) && (num < 108)) {
326
+ this.bg = this.ansi_colors[1][(num - 100)];
327
+ }
328
+ else if (num === 38 || num === 48) {
329
+ if (sgr_cmds.length > 0) {
330
+ let is_foreground = (num === 38);
331
+ let mode_cmd = sgr_cmds.shift();
332
+ if (mode_cmd === '5' && sgr_cmds.length > 0) {
333
+ let palette_index = parseInt(sgr_cmds.shift(), 10);
334
+ if (palette_index >= 0 && palette_index <= 255) {
335
+ if (is_foreground)
336
+ this.fg = this.palette_256[palette_index];
337
+ else
338
+ this.bg = this.palette_256[palette_index];
339
+ }
340
+ }
341
+ if (mode_cmd === '2' && sgr_cmds.length > 2) {
342
+ let r = parseInt(sgr_cmds.shift(), 10);
343
+ let g = parseInt(sgr_cmds.shift(), 10);
344
+ let b = parseInt(sgr_cmds.shift(), 10);
345
+ if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
346
+ let c = { rgb: [r, g, b], class_name: 'truecolor' };
347
+ if (is_foreground)
348
+ this.fg = c;
349
+ else
350
+ this.bg = c;
351
+ }
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+ transform_to_html(fragment) {
358
+ let txt = fragment.text;
359
+ if (txt.length === 0)
360
+ return txt;
361
+ txt = this.escape_txt_for_html(txt);
362
+ if (!fragment.bold && !fragment.italic && !fragment.faint && !fragment.underline && fragment.fg === null && fragment.bg === null)
363
+ return txt;
364
+ let styles = [];
365
+ let classes = [];
366
+ let fg = fragment.fg;
367
+ let bg = fragment.bg;
368
+ if (fragment.bold)
369
+ styles.push(this._boldStyle);
370
+ if (fragment.faint)
371
+ styles.push(this._faintStyle);
372
+ if (fragment.italic)
373
+ styles.push(this._italicStyle);
374
+ if (fragment.underline)
375
+ styles.push(this._underlineStyle);
376
+ if (!this._use_classes) {
377
+ if (fg)
378
+ styles.push(`color:rgb(${fg.rgb.join(',')})`);
379
+ if (bg)
380
+ styles.push(`background-color:rgb(${bg.rgb})`);
381
+ }
382
+ else {
383
+ if (fg) {
384
+ if (fg.class_name !== 'truecolor') {
385
+ classes.push(`${fg.class_name}-fg`);
386
+ }
387
+ else {
388
+ styles.push(`color:rgb(${fg.rgb.join(',')})`);
389
+ }
390
+ }
391
+ if (bg) {
392
+ if (bg.class_name !== 'truecolor') {
393
+ classes.push(`${bg.class_name}-bg`);
394
+ }
395
+ else {
396
+ styles.push(`background-color:rgb(${bg.rgb.join(',')})`);
397
+ }
398
+ }
399
+ }
400
+ let class_string = '';
401
+ let style_string = '';
402
+ if (classes.length)
403
+ class_string = ` class="${classes.join(' ')}"`;
404
+ if (styles.length)
405
+ style_string = ` style="${styles.join(';')}"`;
406
+ return `<span${style_string}${class_string}>${txt}</span>`;
407
+ }
408
+ ;
409
+ process_hyperlink(pkt) {
410
+ let parts = pkt.url.split(':');
411
+ if (parts.length < 1)
412
+ return '';
413
+ if (!this._url_allowlist[parts[0]])
414
+ return '';
415
+ let result = `<a href="${this.escape_txt_for_html(pkt.url)}">${this.escape_txt_for_html(pkt.text)}</a>`;
416
+ return result;
417
+ }
418
+ }
419
+ function rgx(tmplObj, ...subst) {
420
+ let regexText = tmplObj.raw[0];
421
+ let wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
422
+ let txt2 = regexText.replace(wsrgx, '');
423
+ return new RegExp(txt2);
424
+ }
425
+ function rgxG(tmplObj, ...subst) {
426
+ let regexText = tmplObj.raw[0];
427
+ let wsrgx = /^\s+|\s+\n|\s*#[\s\S]*?\n|\n/gm;
428
+ let txt2 = regexText.replace(wsrgx, '');
429
+ return new RegExp(txt2, 'g');
430
+ }
431
+ var templateObject_1, templateObject_2, templateObject_3;
@@ -0,0 +1,125 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover">
6
+ <meta name="theme-color" content="#0d1117">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <link rel="apple-touch-icon" href="/icon.png">
10
+ <link rel="manifest" href="/manifest.json">
11
+ <title>myhi</title>
12
+ <style>
13
+ * { box-sizing: border-box; margin: 0; padding: 0; }
14
+ html, body {
15
+ height: 100%;
16
+ background: #0d1117;
17
+ color: #e6edf3;
18
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
19
+ }
20
+ body {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ padding: env(safe-area-inset-top) env(safe-area-inset-right)
25
+ env(safe-area-inset-bottom) env(safe-area-inset-left);
26
+ }
27
+
28
+ .card {
29
+ width: 100%;
30
+ max-width: 340px;
31
+ padding: 2.5rem 2rem;
32
+ display: flex;
33
+ flex-direction: column;
34
+ align-items: center;
35
+ gap: 1.5rem;
36
+ }
37
+
38
+ .logo {
39
+ font-size: 2.2rem;
40
+ font-weight: 800;
41
+ letter-spacing: -1px;
42
+ }
43
+ .logo span { color: #7c3aed; }
44
+
45
+ form {
46
+ width: 100%;
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 0.85rem;
50
+ }
51
+
52
+ input[type=password] {
53
+ width: 100%;
54
+ background: #161b22;
55
+ border: 1.5px solid #21262d;
56
+ border-radius: 10px;
57
+ padding: 0.85rem 1rem;
58
+ color: #e6edf3;
59
+ font-size: 1.1rem;
60
+ text-align: center;
61
+ letter-spacing: 0.2em;
62
+ outline: none;
63
+ transition: border-color 0.15s;
64
+ -webkit-appearance: none;
65
+ }
66
+ input[type=password]:focus { border-color: #7c3aed; }
67
+ input[type=password]::placeholder { letter-spacing: normal; font-size: 0.9rem; color: #4a5568; }
68
+
69
+ button {
70
+ width: 100%;
71
+ background: #7c3aed;
72
+ color: #fff;
73
+ border: none;
74
+ border-radius: 10px;
75
+ padding: 0.85rem;
76
+ font-size: 1rem;
77
+ font-weight: 600;
78
+ cursor: pointer;
79
+ transition: background 0.15s;
80
+ -webkit-appearance: none;
81
+ }
82
+ button:active { background: #6d28d9; }
83
+
84
+ .error {
85
+ font-size: 0.82rem;
86
+ color: #f85149;
87
+ text-align: center;
88
+ min-height: 1.2em;
89
+ }
90
+
91
+ .hint {
92
+ font-size: 0.75rem;
93
+ color: #4a5568;
94
+ text-align: center;
95
+ line-height: 1.5;
96
+ }
97
+ </style>
98
+ </head>
99
+ <body>
100
+ <div class="card">
101
+ <div class="logo">my<span>hi</span></div>
102
+
103
+ <form method="POST" action="/login">
104
+ <input type="password" name="password" placeholder="输入密码" autofocus autocomplete="current-password">
105
+ <div class="error" id="err">
106
+ <!-- populated by JS if ?error=1 -->
107
+ </div>
108
+ <button type="submit">进入</button>
109
+ </form>
110
+
111
+ <div class="hint">密码见服务器启动日志<br>或编辑 ~/.myhi/password<br>多用户模式:编辑 ~/.myhi/roles.json</div>
112
+ </div>
113
+
114
+ <script>
115
+ const errType = new URLSearchParams(location.search).get('error');
116
+ if (errType === 'locked') {
117
+ document.getElementById('err').textContent = '登录尝试过多,请 5 分钟后重试';
118
+ } else if (errType) {
119
+ document.getElementById('err').textContent = '密码错误,请重试';
120
+ }
121
+ // Auto-submit on Enter (already default form behavior, just ensure focus)
122
+ document.querySelector('input').focus();
123
+ </script>
124
+ </body>
125
+ </html>
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "myhi",
3
+ "short_name": "myhi",
4
+ "description": "Web terminal via Tailscale",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#0d1117",
8
+ "theme_color": "#0d1117",
9
+ "orientation": "any",
10
+ "icons": [
11
+ { "src": "/icon.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }
12
+ ]
13
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }