Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
582 lines (564 loc) · 40.2 KB
/
Copy pathindex.html
File metadata and controls
582 lines (564 loc) · 40.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GTNH Monitor</title>
<!--OG_TAGS-->
<!-- ME Interface block icon (appliedenergistics2:tile.BlockInterface, damage 0) -
there's also an "ME Interface" cable-part variant (item.ItemMultiPart~440)
in the export, but this is the standalone block. Served through the
page's own /icons route rather than a separate static file, so it
depends on images.zip being present the same way every other icon
on the page does - if that's ever missing, the favicon just silently
falls back to nothing, same as any other unmatched icon. Asked for by
item key, and as ?notification=1's still 192px PNG, so it's a PNG
whatever format the game data's icons are. -->
<link rel="icon" type="image/png" href="/icons?key=appliedenergistics2%3Atile.BlockInterface%3A0&notification=1">
<script defer src="/static/vendor/chart-4.4.4.umd.js"></script>
<script defer src="/static/vendor/chartjs-adapter-date-fns-3.0.0.bundle.min.js"></script>
<script src="/static/boot.js?v=__ASSET_VERSION__"></script>
<link rel="stylesheet" href="/static/app.css?v=__ASSET_VERSION__">
<!-- Lets the page be added to a phone's home screen as a standalone app -
iOS only allows web notifications from a page opened that way. -->
<link rel="manifest" href="/static/manifest.json?v=__ASSET_VERSION__">
<link rel="apple-touch-icon" href="/icons?key=appliedenergistics2%3Atile.BlockInterface%3A0&notification=1">
<meta name="apple-mobile-web-app-capable" content="yes">
</head>
<body>
<div class="top-bar">
<h1>GTNH Monitor</h1>
<div class="tab-bar">
<button class="tab-btn active" id="tabBtnCrafts" data-action="switch-tab" data-tab="crafts">Crafts</button>
<button class="tab-btn" id="tabBtnPower" data-action="switch-tab" data-tab="power">Power<span class="tab-fill" id="tabFillPower"></span></button>
<button class="tab-btn" id="tabBtnNetwork" data-action="switch-tab" data-tab="network">Network</button>
</div>
<div class="top-bar-right">
<!-- A real form with a username/password pair, so password managers
offer to save the token and fill it in. method="post" keeps the
token out of the URL should the script ever not intercept it. -->
<form id="signInForm" class="sign-in-form" method="post">
<input type="text" name="username" autocomplete="username"
value="GTNH Monitor" hidden>
<input type="password" id="accessTokenInput" class="craft-key-input"
name="token" placeholder="Access token" autocomplete="current-password"
title="Enter your personal access token to sign in">
<button type="submit" class="notif-btn" id="authBtn">Sign in</button>
</form>
<div class="settings-wrap" id="settingsWrap">
<button class="notif-btn settings-btn" id="settingsBtn" title="Settings"
aria-label="Settings" aria-haspopup="true" aria-expanded="false"
data-action="toggle-settings"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g fill="currentColor"><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(0 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(45 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(90 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(135 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(180 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(225 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(270 8 8)"/><rect x="6.9" y="0.6" width="2.2" height="3.2" rx="0.5" transform="rotate(315 8 8)"/><path fill-rule="evenodd" d="M8 3.1a4.9 4.9 0 1 0 0 9.8 4.9 4.9 0 0 0 0-9.8zm0 2.8a2.1 2.1 0 1 1 0 4.2 2.1 2.1 0 0 1 0-4.2z"/></g></svg><span class="update-dot" id="settingsUpdateDot" hidden></span></button>
<div class="settings-menu" id="settingsMenu" role="menu" style="display:none;">
<div class="settings-menu-user" id="settingsUserRow" hidden><span id="settingsUser"></span><span id="settingsVersion"></span></div>
<div class="theme-row">
Theme
<div class="theme-seg" role="radiogroup" aria-label="Theme">
<button role="radio" aria-checked="false" title="Light" aria-label="Light"
data-action="set-theme" data-theme-choice="light"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.6M8 13.4V15M1 8h1.6M13.4 8H15M3.05 3.05l1.13 1.13M11.82 11.82l1.13 1.13M3.05 12.95l1.13-1.13M11.82 4.18l1.13-1.13"/></g></svg></button>
<button role="radio" aria-checked="false" title="Dark" aria-label="Dark"
data-action="set-theme" data-theme-choice="dark"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><path fill="currentColor" d="M6.2 1.3a6.9 6.9 0 1 0 8.5 8.5A5.6 5.6 0 0 1 6.2 1.3z"/></svg></button>
<button role="radio" aria-checked="false" title="Match system" aria-label="Match system"
data-action="set-theme" data-theme-choice="auto"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><circle cx="8" cy="8" r="6.2" fill="none" stroke="currentColor" stroke-width="1.5"/><path fill="currentColor" d="M8 1.8a6.2 6.2 0 0 0 0 12.4z"/></svg></button>
</div>
</div>
<button class="settings-menu-item settings-toggle" id="notifToggle" role="menuitemcheckbox"
aria-checked="false" data-action="toggle-notifications">
<span class="settings-toggle-label">
Notifications
<span class="settings-toggle-note" id="notifNote"></span>
</span>
<span class="switch" aria-hidden="true"></span>
</button>
<button class="settings-menu-item" id="adminBtn" role="menuitem" style="display:none;"
data-action="open-admin">Manage users</button>
<button class="settings-menu-item" id="gamedataBtn" role="menuitem" style="display:none;"
data-action="open-gamedata">Game data</button>
<button class="settings-menu-item" id="patternCheckBtn" role="menuitem" style="display:none;"
data-action="open-pattern-check">Pattern check</button>
<button class="settings-menu-item" id="agentsBtn" role="menuitem" style="display:none;"
data-action="open-agents">Status<span class="menu-note" id="agentsBtnNote" hidden></span></button>
<button class="settings-menu-item" id="webhooksBtn" role="menuitem" style="display:none;"
data-action="open-webhooks">Webhooks</button>
<button class="settings-menu-item" id="backupBtn" role="menuitem" style="display:none;"
data-action="open-backups">Backups</button>
<button class="settings-menu-item danger" id="signOutBtn" role="menuitem"
data-action="sign-out">Sign out</button>
</div>
</div>
</div>
</div>
<!-- Signed out of a private server (auth.js): instead of the tabs. -->
<div id="privateNotice" class="private-notice" style="display:none;">
<h2>This base is private</h2>
<p>Sign in with the access token one of its admins gave you.</p>
</div>
<div class="stale-banner" id="loadError" role="alert"></div>
<!-- Admins: computers whose scripts this server has outgrown (agents.js). -->
<div class="gamedata-note scripts-notice" id="scriptsNotice" hidden>
<span id="scriptsNoticeText"></span>
<button class="range-btn" data-action="scripts-notice-update">Update…</button>
<a id="scriptsNoticeChanges" target="_blank" rel="noopener">What changed</a>
<button class="history-close-btn note-dismiss" data-action="dismiss-scripts-notice" title="Don't show this again for this version">×</button>
</div>
<!-- Admins: game data that looks older than the game (gamedata.js). -->
<div class="gamedata-note scripts-notice" id="gamedataNotice" hidden>
<span id="gamedataNoticeText"></span>
<button class="range-btn" id="gamedataNoticeSwitch" data-action="gamedata-notice-switch"></button>
<button class="history-close-btn note-dismiss" data-action="dismiss-gamedata-notice" title="Don't show this again for this suggestion">×</button>
</div>
<!-- Operators: patterns the last GTNH upgrade's recipe changes affect, or
for admins, why that couldn't be checked (recipe-check.js). -->
<div class="gamedata-note scripts-notice" id="recipeNotice" hidden>
<span id="recipeNoticeText"></span>
<button class="range-btn" id="recipeNoticeShow" data-action="recipe-notice-show">Show patterns</button>
<button class="history-close-btn note-dismiss" data-action="dismiss-recipe-notice" title="Don't show this again for this upgrade">×</button>
</div>
<div id="craftsTab">
<div class="sub" id="sourceLine">Waiting for data…</div>
<div class="stale-banner" id="staleBanner">No update from the game in a while — is the computer running / online?</div>
<div id="root"></div>
<details class="craft-history" id="craftHistory">
<summary>Recent crafts</summary>
<div class="craft-history-tools">
<div class="range-buttons" id="craftHistoryStatus">
<button class="range-btn active" data-action="history-status" data-status="">All</button>
<button class="range-btn" data-action="history-status" data-status="incomplete">Incomplete</button>
<button class="range-btn" data-action="history-status" data-status="auto">Auto</button>
</div>
<input type="search" id="craftHistoryFilter" class="modal-filter" placeholder="Filter…" title="Filter by name, as in the network search (@mod, -exclude, a|b)" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off">
</div>
<div id="craftHistoryList" class="craft-history-list"></div>
<div class="history-loading" id="craftHistoryLoading" hidden>Loading older crafts…</div>
</details>
<details class="craft-history craft-stats" id="craftStats">
<summary>Statistics</summary>
<div class="craft-history-tools">
<div class="range-buttons" id="craftStatsRange">
<button class="range-btn" data-action="stats-range" data-range="day">Day</button>
<button class="range-btn active" data-action="stats-range" data-range="week">Week</button>
<button class="range-btn" data-action="stats-range" data-range="month">Month</button>
<button class="range-btn" data-action="stats-range" data-range="all">All</button>
</div>
</div>
<div id="craftStatsBody"></div>
</details>
</div>
<div id="powerTab" style="display:none;">
<div class="sub" id="powerSourceLine">Waiting for data…</div>
<div class="range-buttons power-machines" id="powerMachines" hidden></div>
<div class="power-head">
<div class="power-current" id="powerCurrent">Waiting for data…</div>
<div class="range-buttons" id="powerRanges">
<button class="range-btn" data-range="hour" data-action="power-range">Hour</button>
<button class="range-btn active" data-range="day" data-action="power-range">Day</button>
<button class="range-btn" data-range="week" data-action="power-range">Week</button>
<button class="range-btn" data-range="month" data-action="power-range">Month</button>
<button class="range-btn" data-range="lifetime" data-action="power-range">Lifetime</button>
</div>
</div>
<div class="connect-note" id="powerConnectNote" hidden>
<span>No power readings yet - is power_monitor.lua running on a computer?</span>
<button class="range-btn admin-only" data-action="connect-computer">Connect a computer</button>
<span class="non-admin-only">An admin can connect one under Settings → Status.</span>
</div>
<div class="chart-wrap" id="powerChartWrap">
<canvas id="powerChart"></canvas>
</div>
<div class="empty" id="powerEmpty" style="display:none;">No power readings in this range.</div>
</div>
<div id="networkTab" style="display:none;">
<!-- First, so it stacks with any page-wide notice above the tabs. -->
<div class="gamedata-note" id="networkTerminalNote" hidden>
<span>Connect an ME Interface Terminal to see the crafting plan before you request a craft, and each job's steps on the Crafts tab.</span>
<a id="networkTerminalHow" target="_blank" rel="noopener">How</a>
<button class="history-close-btn note-dismiss" data-action="dismiss-terminal-note" title="Don't show this again">×</button>
</div>
<div class="sub" id="networkSourceLine">Waiting for data…</div>
<div class="gamedata-note" id="networkGameDataNote" hidden>
<span>No GTNH version is picked yet, so items have no icons and the network scanner has no item list.</span>
<button class="range-btn" id="networkGameDataBtn" data-action="open-gamedata">Pick the GTNH version</button>
<span id="networkGameDataAsk">An admin can pick it under Settings → Game data.</span>
</div>
<div id="craftRequestsSection"></div>
<details class="stock-rules" id="stockRules" hidden>
<summary id="stockRulesSummary">Stock rules</summary>
<div id="stockRulesList" class="craft-history-list"></div>
</details>
<div class="network-head">
<div class="network-search-wrap">
<input type="text" id="networkSearch" class="network-search" placeholder="Search items…" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off">
<div id="networkSearchHighlight" class="network-search-highlight" aria-hidden="true"></div>
</div>
<div class="network-controls">
<div class="range-buttons">
<button class="range-btn" data-sort="name" data-action="network-sort">Name</button>
<button class="range-btn active" data-sort="size" data-action="network-sort">Quantity</button>
<button class="range-btn" data-sort="change" data-action="network-sort" title="Most used up first, over the range beside it">Change</button>
</div>
<div class="range-buttons" id="networkChangeRange" hidden>
<button class="range-btn active" data-range="day" data-action="network-change-range">Day</button>
<button class="range-btn" data-range="week" data-action="network-change-range">Week</button>
</div>
<div class="range-buttons view-toggle">
<button class="range-btn active" data-view="grid" data-action="network-view" title="Grid" aria-label="Grid"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g fill="currentColor"><rect x="1.5" y="1.5" width="5.5" height="5.5" rx="1"/><rect x="9" y="1.5" width="5.5" height="5.5" rx="1"/><rect x="1.5" y="9" width="5.5" height="5.5" rx="1"/><rect x="9" y="9" width="5.5" height="5.5" rx="1"/></g></svg></button>
<button class="range-btn" data-view="list" data-action="network-view" title="List, with item names" aria-label="List"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g fill="currentColor"><rect x="1.5" y="2" width="3" height="3" rx="0.7"/><rect x="6" y="2.6" width="8.5" height="1.8" rx="0.9"/><rect x="1.5" y="6.5" width="3" height="3" rx="0.7"/><rect x="6" y="7.1" width="8.5" height="1.8" rx="0.9"/><rect x="1.5" y="11" width="3" height="3" rx="0.7"/><rect x="6" y="11.6" width="8.5" height="1.8" rx="0.9"/></g></svg></button>
</div>
</div>
</div>
<div id="networkList" class="network-grid" aria-label="Items - arrow keys move, Enter opens, C crafts, P pins"></div>
<div id="networkEmpty" style="display:none;">
<div class="connect-note">
<span>No network scan yet - is network_browser.lua running on a computer?</span>
<button class="range-btn admin-only" data-action="connect-computer">Connect a computer</button>
<span class="non-admin-only">An admin can connect one under Settings → Status.</span>
</div>
</div>
</div>
<div id="toastContainer" class="toast-container"></div>
<div id="craftRequestModal" class="modal-overlay" style="display:none;">
<div class="modal-box">
<div class="modal-header">
<img id="craftRequestIcon" class="modal-icon" src="" alt="" style="display:none;">
<div id="craftRequestName" class="modal-item-name"></div>
</div>
<label class="modal-label">Quantity</label>
<input type="number" id="craftRequestAmount" class="modal-amount-input" value="1" min="1" step="1" spellcheck="false">
<div id="craftRequestAmountPreview" class="modal-amount-preview" style="display:none;"></div>
<div id="craftPlan" class="craft-plan" hidden>
<div class="craft-plan-head">
<div class="range-buttons craft-plan-views">
<button class="range-btn active" data-view="list" data-action="plan-view">List</button>
<button class="range-btn" data-view="tree" data-action="plan-view">Tree</button>
</div>
<div class="craft-plan-tools">
<input type="search" id="craftPlanFilter" class="modal-filter" placeholder="Filter…" title="Filter by name, as in the network search (-exclude, a|b)" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off">
<button class="range-btn craft-plan-hide" id="craftPlanHide" data-action="plan-hide-available" aria-pressed="false" title="Show only the steps leading to something missing" hidden>Hide all available</button>
<details id="craftPlanRules" class="plan-rules" hidden>
<summary id="craftPlanRulesSummary" class="range-btn plan-rules-btn"></summary>
<div id="craftPlanRulesList" class="plan-rules-list"></div>
</details>
</div>
</div>
<div id="craftPlanSummary" class="craft-plan-summary"></div>
<div id="craftPlanBody" class="craft-plan-body"></div>
<div class="craft-plan-foot">An estimate from your patterns and the last network scan. AE2 may plan differently (ore-dictionary substitutions; interface priorities, which OpenComputers can't see).</div>
</div>
<div id="craftPlanNote" class="modal-note craft-plan-note" hidden></div>
<div id="craftRequestError" class="modal-error" style="display:none;"></div>
<div class="modal-buttons">
<button class="modal-btn secondary" data-action="close-craft-request">Cancel</button>
<button class="modal-btn primary" id="craftRequestSubmitBtn" data-action="submit-craft-request">Request</button>
</div>
</div>
</div>
<div id="cancelConfirmModal" class="modal-overlay" style="display:none;">
<div class="modal-box">
<div class="modal-header">
<div class="modal-item-name">Cancel this craft?</div>
</div>
<div id="cancelConfirmSub" class="modal-note"></div>
<div id="cancelConfirmError" class="modal-error" style="display:none;"></div>
<div class="modal-buttons">
<button class="modal-btn secondary" data-action="close-cancel-confirm">Keep going</button>
<button class="modal-btn cancel-confirm-btn" id="cancelConfirmSubmitBtn" data-action="submit-cancel">Cancel craft</button>
</div>
</div>
</div>
<!-- Before itemHistoryModal: a cell click opens history on top of it. -->
<div id="ingredientsModal" class="modal-overlay" style="display:none;">
<div class="modal-box ingredients-box">
<div class="modal-header">
<div class="history-name-wrap">
<div id="ingredientsTitle" class="craft-title"></div>
<div id="ingredientsCpu" class="cpu-id"></div>
</div>
<input type="search" id="ingredientsFilter" class="modal-filter" placeholder="Filter…" title="Filter by name, as in the network search (@mod in the grid, -exclude, a|b)" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off">
<button class="history-close-btn" data-action="close-ingredients" title="Close">×</button>
</div>
<div id="ingredientsProgress" class="ingredients-progress"></div>
<div class="range-buttons ingredients-views">
<button class="range-btn active" data-view="grid" data-action="ingredients-view">Grid</button>
<button class="range-btn" data-view="tree" data-action="ingredients-view" title="The job step by step, rebuilt from the network's patterns">Tree</button>
</div>
<div id="ingredientsNote" class="modal-note" style="display:none;"></div>
<div id="ingredientsGrid" class="ingredients-grid"></div>
<div id="ingredientsTreeWrap" class="ingredients-tree-wrap" hidden>
<div id="ingredientsStuck" class="plan-missing-strip job-stuck-strip" hidden></div>
<div id="ingredientsTree" class="craft-plan-body ingredients-tree"></div>
</div>
</div>
</div>
<div id="itemHistoryModal" class="modal-overlay" style="display:none;">
<div class="modal-box history-box">
<div class="modal-header">
<img id="itemHistoryIcon" class="modal-icon" src="" alt="" style="display:none;">
<div class="history-name-wrap">
<div id="itemHistoryName" class="modal-item-name history-name"></div>
<div id="itemHistoryCurrent" class="history-current"></div>
<div id="itemHistoryTrend" class="history-trend"></div>
</div>
<button class="history-pin-btn" id="itemHistoryPinBtn" data-action="history-pin" title="Pin this item"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g transform="rotate(45 8 8)" fill="currentColor"><rect x="5" y="1" width="6" height="2" rx="0.6"/><path d="M6.2 3h3.6l1 5H5.2z"/><rect x="3.5" y="7.6" width="9" height="1.8" rx="0.9"/><rect x="7.35" y="9" width="1.3" height="6" rx="0.65"/></g></svg></button>
<button class="history-close-btn" data-action="close-history" title="Close">×</button>
</div>
<div id="itemHistoryInfo" class="history-info" hidden>
<div id="itemHistoryShort" class="history-short" hidden></div>
<details id="itemHistoryDetails" class="history-details" hidden>
<summary class="history-summary"><span id="itemHistorySummary" class="history-summary-text"></span> <span id="itemHistoryMore" class="history-summary-more"></span><span class="history-summary-open">Description</span></summary>
<div id="itemHistoryDescription" class="game-text history-description"></div>
<div class="history-shift-row">
<button id="itemHistoryShiftBtn" class="range-btn history-shift-btn" data-action="history-shift" aria-pressed="false" title="Show what the game shows with Shift held" hidden>Shift</button>
</div>
</details>
<div id="itemHistoryOre" class="history-ore"></div>
</div>
<div class="range-buttons" id="itemHistoryRangeButtons">
<button class="range-btn active" data-range="day" data-action="history-range">Day</button>
<button class="range-btn" data-range="week" data-action="history-range">Week</button>
<button class="range-btn" data-range="month" data-action="history-range">Month</button>
<button class="range-btn" data-range="lifetime" data-action="history-range">Lifetime</button>
</div>
<div class="chart-wrap history-chart-wrap">
<canvas id="itemHistoryChart"></canvas>
</div>
<div class="empty" id="itemHistoryEmpty" style="display:none;">No history yet for this item.</div>
<div id="stockPanel" class="stock-panel" hidden>
<div class="stock-panel-row" id="stockAlertRow">
<label class="stock-panel-label" for="stockAlertBelow"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><path fill="currentColor" d="M8 1.2a1 1 0 0 1 1 1v.7a4.6 4.6 0 0 1 3.6 4.5v3l1.3 1.7a.6.6 0 0 1-.5 1H2.6a.6.6 0 0 1-.5-1l1.3-1.7v-3A4.6 4.6 0 0 1 7 2.9v-.7a1 1 0 0 1 1-1zM6.2 14.3h3.6a1.8 1.8 0 0 1-3.6 0z"/></svg> Alert me below</label>
<input id="stockAlertBelow" class="modal-amount-input stock-input" type="text" inputmode="text" placeholder="e.g. 10k" spellcheck="false" autocomplete="off">
<button class="modal-btn secondary" data-action="stock-save-alert">Save</button>
<button class="modal-btn secondary" id="stockAlertDelete" data-action="stock-delete-alert" hidden>Remove</button>
</div>
<div class="stock-panel-row" id="stockTargetRow">
<label class="stock-panel-label" for="stockKeep"><svg class="ui-icon" viewBox="0 0 16 16" width="1em" height="1em" aria-hidden="true"><g fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M13.2 7A5.2 5.2 0 0 0 3.7 4.6"/><path d="M3.4 1.9v2.9h2.9"/><path d="M2.8 9a5.2 5.2 0 0 0 9.5 2.4"/><path d="M12.6 14.1v-2.9H9.7"/></g></svg> Keep at least</label>
<input id="stockKeep" class="modal-amount-input stock-input" type="text" inputmode="text" placeholder="e.g. 10k" spellcheck="false" autocomplete="off">
<label class="stock-panel-label" for="stockRefill">refill to</label>
<input id="stockRefill" class="modal-amount-input stock-input" type="text" inputmode="text" placeholder="e.g. 20k" spellcheck="false" autocomplete="off">
<button class="modal-btn secondary" data-action="stock-save-target">Save</button>
<button class="modal-btn secondary" id="stockTargetDelete" data-action="stock-delete-target" hidden>Remove</button>
</div>
<div id="stockTargetNote" class="modal-note stock-panel-note" hidden></div>
<div id="stockError" class="modal-error" hidden></div>
</div>
<button class="modal-btn primary history-craft-btn" id="itemHistoryCraftBtn" style="display:none;" data-action="craft-from-history">Request Craft</button>
</div>
</div>
<div id="adminUsersModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box admin-users-box">
<div class="modal-header">
<div class="modal-item-name">User management</div>
<button class="history-close-btn" data-action="close-admin" title="Close">×</button>
</div>
<div class="status-heading">Visibility</div>
<div class="agent-settings">
<label class="agent-setting"><input type="checkbox" id="privateToggle">
<span>Only signed-in users can see this base
<span class="agent-line">Signed out, the crafts, network and power are hidden, and links to this base show no preview.</span></span></label>
</div>
<div class="status-heading admin-section-heading">Add a user</div>
<div class="admin-form-grid">
<input id="adminUserName" class="modal-amount-input" maxlength="100" placeholder="Display name" autocomplete="off">
<select id="adminUserRole" class="modal-amount-input" aria-describedby="adminRoleHelp">
<option value="viewer">Viewer</option>
<option value="operator" selected>Operator</option>
<option value="admin">Admin</option>
</select>
<button class="modal-btn primary" id="adminCreateUserBtn" data-action="create-user">Create user</button>
</div>
<div id="adminRoleHelp" class="modal-note admin-role-help"></div>
<div id="adminUsersError" class="modal-error" style="display:none;"></div>
<div class="status-heading admin-section-heading">Users <span id="adminUsersCount"></span></div>
<div id="adminUsersList" class="admin-users"></div>
</div>
</div>
<div id="gamedataModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box">
<div class="modal-header">
<div class="modal-item-name">Game data</div>
<button class="history-close-btn" data-action="close-gamedata" title="Close">×</button>
</div>
<div class="status-heading">Version</div>
<div class="modal-note">Item icons and the network scanner's item list, for the GTNH version your server runs. The download is about 300 MB and is used without a restart; the scanner switches on its next scan.</div>
<div id="gamedataCurrent" class="gamedata-current"></div>
<details id="gamedataUnknown" class="gamedata-unknown" hidden>
<summary id="gamedataUnknownSummary"></summary>
<ul id="gamedataUnknownList"></ul>
<div id="gamedataUnpublished" class="modal-note" hidden>
<span id="gamedataUnpublishedText"></span>
<a href="https://github.com/Maselkov/gtnh-craft-monitor/blob/main/tools/icon-export/README.md" target="_blank" rel="noopener">Building game data</a>
</div>
</details>
<label class="gamedata-label" for="gamedataVersion">GTNH version</label>
<select id="gamedataVersion" class="modal-amount-input"></select>
<label class="gamedata-label" for="gamedataTextures">Icon textures</label>
<select id="gamedataTextures" class="modal-amount-input"></select>
<div id="gamedataCredit" class="gamedata-credit" style="display:none;"></div>
<div id="gamedataError" class="modal-error" style="display:none;"></div>
<div id="gamedataProgress" class="gamedata-progress" style="display:none;">
<div class="progress-track"><div class="progress-fill" id="gamedataProgressFill"></div></div>
<div class="progress-label" id="gamedataProgressLabel"></div>
</div>
<div class="modal-buttons" style="margin-top:12px;">
<button class="modal-btn secondary" data-action="refresh-gamedata">Check for new versions</button>
<button class="modal-btn primary" id="gamedataInstallBtn" data-action="install-gamedata">Use these icons</button>
</div>
<div class="status-heading">Pattern check</div>
<div class="modal-note">GTNH doesn't publish its recipe changes. This compares the GregTech recipes of two versions and lists the patterns a change affects: before updating, pick the new version; after, pick the one in use to check against the version before it.</div>
<div class="gamedata-current"><span id="recipeCheckFor"></span>
<a id="recipeCheckForHow" target="_blank" rel="noopener" hidden>How to connect one</a></div>
<div class="modal-buttons">
<button class="modal-btn secondary" id="recipeCheckBtn" data-action="check-recipes">Check patterns</button>
</div>
</div>
</div>
<div id="recipeCheckModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box recipe-check-box">
<div class="modal-header">
<div class="modal-item-name" id="recipeCheckTitle">Pattern check</div>
<button class="history-close-btn" data-action="close-recipe-check" title="Close">×</button>
</div>
<div id="recipeCheckSummary" class="gamedata-current"></div>
<div id="recipeCheckError" class="modal-error" style="display:none;"><span id="recipeCheckErrorText"></span>
<a id="recipeCheckErrorHow" target="_blank" rel="noopener" hidden>How to connect one</a></div>
<div id="recipeCheckList"></div>
</div>
</div>
<div id="backupsModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box">
<div class="modal-header">
<div class="modal-item-name">Backups</div>
<button class="history-close-btn" data-action="close-backups" title="Close">×</button>
</div>
<div class="status-heading">Download</div>
<div class="modal-note">A zip of every database (accounts, pins, craft, power and item history) and the server's keys, taken while it runs.</div>
<div class="modal-buttons">
<button class="modal-btn primary" id="backupDownloadBtn" data-action="download-backup">Download backup</button>
</div>
<div class="status-heading backups-restore-heading">Restore</div>
<div class="modal-note">Replaces everything with the backup's: every database, account, token and key. Anything since the backup was made is lost, and you're signed out if your session is newer than it.</div>
<div class="restore-pick">
<label class="range-btn restore-pick-btn">Choose backup…
<input type="file" id="restoreFile" class="restore-file" accept=".zip,application/zip"></label>
<span id="restoreFileName" class="restore-file-name">No file chosen</span>
</div>
<div id="restoreError" class="modal-error" style="display:none;"></div>
<div class="modal-buttons">
<button class="modal-btn cancel-confirm-btn" id="restoreBtn" data-action="restore-backup" disabled>Restore</button>
</div>
</div>
</div>
<div id="webhooksModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box">
<div class="modal-header">
<div class="modal-item-name">Webhooks</div>
<button class="history-close-btn" data-action="close-webhooks" title="Close">×</button>
</div>
<div class="modal-note" id="webhooksNote"></div>
<textarea id="webhookUrls" class="modal-amount-input webhook-urls" rows="3" spellcheck="false"
autocomplete="off" placeholder="https://discord.com/api/webhooks/…"></textarea>
<div class="agent-settings">
<label class="agent-setting"><input type="checkbox" id="webhookEventCrafts" data-event="crafts">
<span>A pinned craft finishes or stops</span></label>
<label class="agent-setting"><input type="checkbox" id="webhookEventAutocraft" data-event="autocraft">
<span>A keep-in-stock auto-craft fails</span></label>
<label class="agent-setting"><input type="checkbox" id="webhookEventStock" data-event="stock">
<span>A low-stock alert fires
<span class="agent-line">Alerts are each user's own; this posts once per item, without saying whose.</span></span></label>
</div>
<div id="webhooksError" class="modal-error" style="display:none;"></div>
<div class="modal-buttons">
<button class="modal-btn secondary" id="webhookTestBtn" data-action="test-webhooks">Send test</button>
<button class="modal-btn primary" id="webhooksSaveBtn" data-action="save-webhooks">Save</button>
</div>
</div>
</div>
<div id="agentsModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box">
<div class="modal-header">
<div class="modal-item-name">Status</div>
<button class="history-close-btn" data-action="close-agents" title="Close">×</button>
</div>
<div class="status-body">
<div class="status-heading">Server</div>
<div id="serverHealth" class="server-health"></div>
<div class="status-heading computers-heading">Computers</div>
<div id="agentsLatest" class="modal-note"></div>
<div class="agent-settings">
<label class="agent-setting"><input type="checkbox" id="autoRestartToggle" disabled>
<span>Restart scripts that stop
<span class="agent-line">Only scripts enabled on the computer, at most 3 times an hour each. A script stopped with <code>rc <name> stop</code> stays stopped.</span></span></label>
<label class="agent-setting"><input type="checkbox" id="autoUpdateToggle" disabled>
<span>Update computers when the server updates
<span class="agent-line" id="autoUpdateNote"></span></span></label>
</div>
<div id="agentsList" class="agents-list"></div>
<div class="status-heading connect-heading">Connect a computer</div>
<div class="connect-computer">
<div class="modal-note">On an OpenComputers computer with an Internet Card, run this command: pick the scripts it installs, copy it and paste it into the computer with the middle mouse button or Insert.</div>
<div id="connectScripts" class="connect-scripts"></div>
<div id="connectNoScripts" class="agent-line agent-command failed" style="display:none;">Pick at least one script to install.</div>
<div id="connectAddress" hidden>
<label class="modal-label" for="connectServerUrl">This server's address, as the Minecraft server reaches it</label>
<input id="connectServerUrl" class="modal-amount-input" spellcheck="false" autocomplete="off">
</div>
<div id="connectLocalNote" class="agent-line agent-command failed" style="display:none;">OpenComputers blocks local addresses like this one unless the Minecraft server's config allows them: see <a href="https://github.com/Maselkov/gtnh-craft-monitor#scripts-cant-reach-the-server" target="_blank" rel="noopener">Scripts can't reach the server</a>.</div>
<div class="connect-command"><code id="connectCommand"></code><button class="range-btn" data-action="copy-connect">Copy</button></div>
<div class="agent-line">It downloads the installer, which installs the scripts, checks that the computer reaches this server with the API key and saves both, starts the scripts on every boot, and reboots to start them. If the server check fails it says why and doesn't reboot.</div>
<div class="agent-buttons">
<button class="range-btn" id="connectKeyBtn" data-action="toggle-connect-key">Show API key</button>
<button class="range-btn" id="connectAddressBtn" data-action="edit-connect-address" title="If the Minecraft server reaches this server at another address than the one in the command">Use a different address</button>
</div>
</div>
</div>
</div>
</div>
<div id="setupModal" class="modal-overlay" style="display:none;">
<div class="modal-box">
<div class="modal-header">
<div class="modal-item-name">First-time setup</div>
</div>
<div id="setupForm">
<div class="modal-note">Create the first admin account. The setup code is in the server's log (<code>docker compose logs gtnh-craft-monitor</code>); opening the link printed there fills it in.</div>
<label class="modal-label" for="setupName">Your name</label>
<input id="setupName" class="modal-amount-input" maxlength="100" placeholder="Display name" autocomplete="off">
<label class="modal-label" for="setupCode">Setup code</label>
<input id="setupCode" class="modal-amount-input" autocomplete="off" spellcheck="false">
<div id="setupError" class="modal-error" style="display:none;"></div>
<div class="modal-buttons">
<button class="modal-btn primary" id="setupBtn" data-action="finish-setup">Create admin account</button>
</div>
</div>
<div id="setupResult" style="display:none;">
<div class="modal-note">You're signed in. This is your access token: use it to sign in from any browser, and save it now, in a password manager say. It is shown only once.</div>
<label class="modal-label" for="setupToken">Your access token</label>
<textarea id="setupToken" class="modal-amount-input admin-token-value" readonly></textarea>
<div class="modal-buttons">
<button class="modal-btn secondary" data-action="copy-setup-token">Copy token</button>
<button class="modal-btn primary" id="setupDoneBtn" data-action="close-setup">I saved my token</button>
</div>
</div>
</div>
</div>
<div id="userHistoryModal" class="modal-overlay" style="display:none;">
<div class="modal-box admin-box user-history-box">
<div class="modal-header">
<div class="history-name-wrap">
<div id="userHistoryTitle" class="modal-item-name">User history</div>
<div id="userHistorySummary" class="cpu-id"></div>
</div>
<button class="history-close-btn" data-action="close-user-history" title="Close">×</button>
</div>
<div id="userHistoryFilters" class="range-buttons user-history-filters">
<button class="range-btn active" data-action="user-history-filter" data-filter="all">All</button>
<button class="range-btn" data-action="user-history-filter" data-filter="request">Requests</button>
<button class="range-btn" data-action="user-history-filter" data-filter="cancel">Cancels</button>
<button class="range-btn" data-action="user-history-filter" data-filter="problem">Failed</button>
<button id="userHistoryAuto" class="range-btn user-history-auto" data-action="user-history-auto" hidden
title="Requests their keep-in-stock targets made"></button>
</div>
<div id="userHistoryList" class="craft-history-list user-history-list"></div>
</div>
</div>
<script type="module" src="/static/js/main.js?v=__ASSET_VERSION__"></script>
</body>
</html>