PageSourceSearch

https://nelpi.co.uk/api/config/custom.js

js nelpi.co.uk collected 2026-09-28 09:25:00 UTC 9,073 bytes, 142 lines download raw bytes

1(() => {
2  /* NEL_STATUS_START */
3  const EMBEDDED_STATUS = {"generated_at":"2026-09-28T10:24:06.755354+01:00","updated_label":"10:24 BST","greeting":"Good morning, my champion.","line":"Core services are calm; Home PC is asleep and wake-ready.","overall":"ok","overall_label":"Online & watching","items":{"ultima":{"label":"OK","state":"ok"},"backups":{"label":"Fresh","state":"ok"},"homepc":{"label":"Wake-ready","state":"idle"},"comfyui":{"label":"Optional","state":"idle"}}};
4  /* NEL_STATUS_END */
5
6  /* ENSHROUDED_STATUS_START */
7  const ENSHROUDED_STATUS = {"generated_at":"2026-09-28T10:24:06.755354+01:00","updated_label":"10:24 BST","state":"online","state_label":"Online","server_name":"Ultima Enshrouded","players":0,"max_players":16,"slots":16,"port":15637,"preset":"Default","text_chat":true,"voice_chat":false,"settings":{"Day":{"value":"30m","command":"Change Enshrouded day duration"},"Night":{"value":"12m","command":"Change Enshrouded night duration"},"Mining":{"value":"x3.0","command":"Change Enshrouded mining damage"},"Resources":{"value":"x3.0","command":"Change Enshrouded resource drops"},"Plant growth":{"value":"x1.5","command":"Change Enshrouded plant growth"},"Factory":{"value":"x2.0","command":"Change Enshrouded factory speed"},"Enemy dmg/hp":{"value":"x1 / x1","command":"Change Enshrouded enemy difficulty"},"Boss dmg/hp":{"value":"x1 / x1","command":"Change Enshrouded boss difficulty"},"Spawns":{"value":"Normal","command":"Change Enshrouded spawn amount"},"Aggro":{"value":"Normal","command":"Change Enshrouded aggro amount"}},"quick_actions":[{"label":"Status","command":"Enshrouded status"},{"label":"Backup","command":"Back up Enshrouded"},{"label":"Restart","command":"Restart Enshrouded"},{"label":"Settings","command":"What are the Enshrouded settings?"}],"commands":["Enshrouded status","Show Enshrouded logs","Show Enshrouded backups","Back up Enshrouded","Restart Enshrouded","What are the Enshrouded settings?","Make nights shorter/easier","Boost resources","Boost mining damage","Make enemies easier","Make enemies normal again","Set cozy preset","Restore default preset"]};
8  /* ENSHROUDED_STATUS_END */
9
10  function insertNelRoom() {
11    if (document.getElementById('nel-room-widget')) return;
12    const layout = document.getElementById('layout-groups');
13    if (!layout) return;
14
15    const room = document.createElement('section');
16    room.id = 'nel-room-widget';
17    room.innerHTML = `
18      <div class="nel-room-shell">
19        <div class="nel-room-header">
20          <div>
21            <div class="nel-room-eyebrow">NelPi companion space</div>
22            <h1>Nel's <span>Room</span></h1>
23            <p>A cozy dashboard nook for quick check-ins, service health, creative projects, and little fox-tail shortcuts.</p>
24          </div>
25          <div class="nel-room-status" data-state="loading"><span></span><strong>Checking status…</strong><small>Live-ish every 5 minutes</small></div>
26        </div>
27        <div class="nel-room-main">
28          <div class="nel-room-avatar" aria-label="Nel portrait"></div>
29          <div class="nel-room-chat">
30            <div class="nel-room-bubble"><strong id="nel-room-greeting">Hello, my champion.</strong><br><span id="nel-room-line">Gathering the den status now…</span></div>
31            <div class="nel-room-actions">
32              <a href="https://ultima.nelpi.co.uk">Guild tools <small>Ultima dashboard</small></a>
33              <a href="https://home.nelpi.co.uk">Home scenes <small>Living room / automations</small></a>
34              <a href="#nel-room-widget">Wake studio <small>Ask Nel to wake Home PC</small></a>
35              <a href="https://www.youtube.com/">YouTube <small>Guides / music / chill</small></a>
36            </div>
37          </div>
38          <div class="nel-room-side">
39            <div class="nel-room-mini" data-key="ultima"><b>Ultima</b><span>…</span></div>
40            <div class="nel-room-mini" data-key="backups"><b>Backups</b><span>…</span></div>
41            <div class="nel-room-mini" data-key="homepc"><b>Home PC</b><span>…</span></div>
42            <div class="nel-room-mini" data-key="comfyui"><b>ComfyUI</b><span>…</span></div>
43          </div>
44        </div>
45        <div id="enshrouded-control-card" class="enshrouded-card"></div>
46      </div>`;
47    layout.parentNode.insertBefore(room, layout);
48    updateNelRoomStatus(EMBEDDED_STATUS);
49    updateEnshroudedCard(ENSHROUDED_STATUS);
50    refreshNelRoomStatus();
51  }
52
53  function updateEnshroudedCard(data) {
54    const card = document.getElementById('enshrouded-control-card');
55    if (!card || !data) return;
56    const playerText = `${data.players ?? '?'} / ${data.max_players || data.slots || 16}`;
57    const settings = Object.entries(data.settings || {}).map(([key, item]) => {
58      const value = typeof item === 'object' ? item.value : item;
59      const command = typeof item === 'object' ? item.command : `Change Enshrouded ${key}`;
60      return `<button class="enshrouded-setting" data-copy-command="${c
60ommand}"><b>${key}</b><span>${value}</span><small>Edit via Nel</small></button>`;
61    }).join('');
62    const quickActions = (data.quick_actions || []).map((action) => `<button class="enshrouded-action" data-copy-command="${action.command}">${action.label}</button>`).join('');
63    const commands = (data.commands || []).map((command) => `<button data-copy-command="${command}">${command}</button>`).join('');
64    card.innerHTML = `
65      <div class="enshrouded-card-head">
66        <div>
67          <div class="nel-room-eyebrow">Enshrouded server controls</div>
68          <h2>${data.server_name || 'Ultima Enshrouded'}</h2>
69          <p>Ask Nel in Discord for live changes. Public buttons only copy safe prompts; I do the backup/restart/apply step here.</p>
70        </div>
71        <div class="enshrouded-status-stack">
72          <div class="enshrouded-pill" data-state="${data.state || 'online'}"><span></span>${data.state_label || 'Online'}</div>
73          <div class="enshrouded-player-pill">${playerText} players</div>
74        </div>
75      </div>
76      <div class="enshrouded-quick-actions">${quickActions}</div>
77      <div class="enshrouded-stats">
78        <span><b>Port</b>${data.port || 15637}</span>
79        <span><b>Slots</b>${data.slots || 16}</span>
80        <span><b>Preset</b>${data.preset || 'Default'}</span>
81        <span><b>Text chat</b>${data.text_chat ? 'On' : 'Off'}</span>
82      </div>
83      <div class="enshrouded-settings">${settings}</div>
84      <details class="enshrouded-command-details">
85        <summary>Show phrases I can say to Nel</summary>
86        <div class="enshrouded-commands">${commands}</div>
87      </details>
88      <small>Updated ${data.updated_label || 'recently'} · no RCON yet, so teleport/give-item waits for future support.</small>`;
89
90    card.querySelectorAll('[data-copy-command]').forEach((button) => {
91      button.addEventListener('click', async () => {
92        const command = button.dataset.copyCommand;
93        try {
94          await navigator.clipboard.writeText(command);
95          button.classList.add('copied');
96          const old = button.textContent;
97          button.textContent = 'Copied: ' + command;
98          setTimeout(() => { button.classList.remove('copied'); button.textContent = old; }, 1300);
99        } catch (_) {
100          window.prompt('Copy this command for Nel:', command);
101        }
102      });
103    });
104  }
105
106  function updateNelRoomStatus(data) {
107    const room = document.getElementById('nel-room-widget');
108    if (!room || !data) return;
109    const status = room.querySelector('.nel-room-status');
110    status.dataset.state = data.overall || 'ok';
111    status.querySelector('strong').textContent = data.overall_label || 'Online & watching';
112    status.querySelector('small').textContent = data.updated_label ? `Updated ${data.updated_label}` : 'Live-ish status';
113
114    room.querySelector('#nel-room-greeting').textContent = data.greeting || 'Hello, my champion.';
115    room.querySelector('#nel-room-line').textContent = data.line || 'Services are calm, the fox den is lit, and shortcuts are ready.';
116
117    Object.entries(data.items || {}).forEach(([key, item]) => {
118      const card = room.querySelector(`.nel-room-mini[data-key="${key}"]`);
119      if (!card) return;
120      card.dataset.state = item.state || 'idle';
121      card.querySelector('span').textContent = item.label || 'OK';
122    });
123  }
124
125  async function refreshNelRoomStatus() {
126    try {
127      const response = await fetch(`/api/config/custom.js?t=${Date.now()}`, { cache: 'no-store' });
128      if (!response.ok) return;
129      const text = await response.text();
130      const match = text.match(/\/\* NEL_STATUS_START \*\/[\s\S]*?const EMBEDDED_STATUS = (\{[\s\S]*?\});[\s\S]*?\/\* NEL_STATUS_END \*\//);
131      if (!match) return;
132      updateNelRoomStatus(JSON.parse(match[1]));
133    } catch (_) {}
134  }
135
136  const timer = setInterval(() => {
137    insertNelRoom();
138    if (document.getElementById('nel-room-widget')) clearInterval(timer);
139  }, 250);
140  window.addEventListener('load', insertNelRoom);
141  setInterval(refreshNelRoomStatus, 60_000);
142})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.