- Convert ANSI-formatted relay logs to HTML for cleaner visualization in initial setup screen
- Add relay logs section to settings interface for improved accessibility
This commit is contained in:
parent
9fe777abd9
commit
6434102635
5 changed files with 149 additions and 2 deletions
|
@ -17,6 +17,7 @@ import '@components/General/Fieldset';
|
||||||
import '@components/General/Input';
|
import '@components/General/Input';
|
||||||
import '@components/LoadingView';
|
import '@components/LoadingView';
|
||||||
import '@components/ProgressSteps';
|
import '@components/ProgressSteps';
|
||||||
|
import '@components/RelayLogs';
|
||||||
|
|
||||||
@customElement('arx-initial-setup')
|
@customElement('arx-initial-setup')
|
||||||
export class InitialSetup extends LitElement {
|
export class InitialSetup extends LitElement {
|
||||||
|
@ -395,8 +396,7 @@ export class InitialSetup extends LitElement {
|
||||||
<iconify-icon icon="mdi:information"></iconify-icon>
|
<iconify-icon icon="mdi:information"></iconify-icon>
|
||||||
Relay is running with PID: ${this.relayStatus.pid}
|
Relay is running with PID: ${this.relayStatus.pid}
|
||||||
</p>
|
</p>
|
||||||
<pre class="relay-logs">${this.relayStatus.logs.slice(-5).join('\n')}</pre>
|
<arx-relay-logs .logs=${this.relayStatus.logs}></arx-relay-logs>`,
|
||||||
`,
|
|
||||||
)}
|
)}
|
||||||
<p>
|
<p>
|
||||||
Having trouble? Our team is here to help if you encounter any
|
Having trouble? Our team is here to help if you encounter any
|
||||||
|
|
39
src/components/RelayLogs.ts
Normal file
39
src/components/RelayLogs.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { ansiToLitHtml } from '@/utils/ansiToLitHtml';
|
||||||
|
import { LitElement, css, html } from 'lit';
|
||||||
|
import { customElement, property } from 'lit/decorators.js';
|
||||||
|
import { map } from 'lit/directives/map.js';
|
||||||
|
|
||||||
|
@customElement('arx-relay-logs')
|
||||||
|
export class RelayLogs extends LitElement {
|
||||||
|
static override styles = css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
font-family: var(--font-family, "Inter", system-ui, sans-serif);
|
||||||
|
margin: 0 auto;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: var(--color-base-content);
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay-logs {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
overflow-x: auto;
|
||||||
|
max-height: 200px;
|
||||||
|
padding: 1rem;
|
||||||
|
border: 2px solid var(--color-base-300);
|
||||||
|
border-radius: var(--radius-box);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background-color: var(--color-code-block);
|
||||||
|
color: var(--color-code-block-content);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
@property({ type: Array }) logs: string[] = [];
|
||||||
|
|
||||||
|
override render() {
|
||||||
|
return html`
|
||||||
|
<pre class="relay-logs">${map(this.logs, (log) => ansiToLitHtml(log))}</pre>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import '@components/General/Button';
|
||||||
import '@components/General/Card';
|
import '@components/General/Card';
|
||||||
import '@components/General/Fieldset';
|
import '@components/General/Fieldset';
|
||||||
import '@components/General/Input';
|
import '@components/General/Input';
|
||||||
|
import '@components/RelayLogs';
|
||||||
|
|
||||||
@customElement('arx-settings')
|
@customElement('arx-settings')
|
||||||
export class EveSettings extends LitElement {
|
export class EveSettings extends LitElement {
|
||||||
|
@ -61,11 +62,22 @@ export class EveSettings extends LitElement {
|
||||||
@state() private profile: NDKUserProfile | undefined;
|
@state() private profile: NDKUserProfile | undefined;
|
||||||
@state() private error: string | undefined;
|
@state() private error: string | undefined;
|
||||||
@state() private darkMode = false;
|
@state() private darkMode = false;
|
||||||
|
@state() private relayStatus: { running: boolean; pid: number | null; logs: string[] } = {
|
||||||
|
running: false,
|
||||||
|
pid: null,
|
||||||
|
logs: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
private async updateRelayStatus() {
|
||||||
|
this.relayStatus = await window.relay.getStatus();
|
||||||
|
if (this.relayStatus.running) setTimeout(() => this.updateRelayStatus(), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
protected override async firstUpdated() {
|
protected override async firstUpdated() {
|
||||||
try {
|
try {
|
||||||
this.profile = await getUserProfile();
|
this.profile = await getUserProfile();
|
||||||
this.darkMode = localStorage.getItem('darkMode') === 'true';
|
this.darkMode = localStorage.getItem('darkMode') === 'true';
|
||||||
|
this.updateRelayStatus();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error = 'Failed to load profile';
|
this.error = 'Failed to load profile';
|
||||||
|
@ -176,6 +188,14 @@ export class EveSettings extends LitElement {
|
||||||
>
|
>
|
||||||
</arx-button>
|
</arx-button>
|
||||||
</arx-card>
|
</arx-card>
|
||||||
|
<arx-card>
|
||||||
|
<arx-fieldset legend="Relay">
|
||||||
|
<p>
|
||||||
|
${this.relayStatus.running ? `Relay is running. PID: ${this.relayStatus.pid}` : 'Relay is not running'}
|
||||||
|
</p>
|
||||||
|
<arx-relay-logs .logs=${this.relayStatus.logs}></arx-relay-logs>
|
||||||
|
</arx-fieldset>
|
||||||
|
</arx-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
--color-warning-content: oklch(41% 0.112 45.904);
|
--color-warning-content: oklch(41% 0.112 45.904);
|
||||||
--color-error: oklch(70% 0.191 22.216);
|
--color-error: oklch(70% 0.191 22.216);
|
||||||
--color-error-content: oklch(39% 0.141 25.723);
|
--color-error-content: oklch(39% 0.141 25.723);
|
||||||
|
--color-code-block: oklch(95% 0.038 75.164);
|
||||||
|
--color-code-block-content: oklch(40% 0.123 38.172);
|
||||||
|
|
||||||
--radius-selector: 1rem;
|
--radius-selector: 1rem;
|
||||||
--radius-field: 0.5rem;
|
--radius-field: 0.5rem;
|
||||||
|
@ -68,6 +70,8 @@ body.dark {
|
||||||
--color-warning-content: oklch(19.106% 0.026 112.757);
|
--color-warning-content: oklch(19.106% 0.026 112.757);
|
||||||
--color-error: oklch(68.22% 0.206 24.43);
|
--color-error: oklch(68.22% 0.206 24.43);
|
||||||
--color-error-content: oklch(13.644% 0.041 24.43);
|
--color-error-content: oklch(13.644% 0.041 24.43);
|
||||||
|
--color-code-block: oklch(20% 0.05 270);
|
||||||
|
--color-code-block-content: oklch(90% 0.076 70.697);
|
||||||
--radius-selector: 1rem;
|
--radius-selector: 1rem;
|
||||||
--radius-field: 0.5rem;
|
--radius-field: 0.5rem;
|
||||||
--radius-box: 1rem;
|
--radius-box: 1rem;
|
||||||
|
|
84
src/utils/ansiToLitHtml.ts
Normal file
84
src/utils/ansiToLitHtml.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||||
|
|
||||||
|
export function ansiToLitHtml(text: string) {
|
||||||
|
const textColors: Record<string, string> = {
|
||||||
|
'30': 'black',
|
||||||
|
'31': 'red',
|
||||||
|
'32': 'green',
|
||||||
|
'33': 'yellow',
|
||||||
|
'34': 'blue',
|
||||||
|
'35': 'magenta',
|
||||||
|
'36': 'cyan',
|
||||||
|
'37': 'white',
|
||||||
|
'90': 'gray',
|
||||||
|
'91': 'lightred',
|
||||||
|
'92': 'lightgreen',
|
||||||
|
'93': 'lightyellow',
|
||||||
|
'94': 'lightblue',
|
||||||
|
'95': 'lightmagenta',
|
||||||
|
'96': 'lightcyan',
|
||||||
|
'97': 'white',
|
||||||
|
};
|
||||||
|
|
||||||
|
const bgColors: Record<string, string> = {
|
||||||
|
'40': 'black',
|
||||||
|
'41': 'red',
|
||||||
|
'42': 'green',
|
||||||
|
'43': 'yellow',
|
||||||
|
'44': 'blue',
|
||||||
|
'45': 'magenta',
|
||||||
|
'46': 'cyan',
|
||||||
|
'47': 'white',
|
||||||
|
'100': 'gray',
|
||||||
|
'101': 'lightred',
|
||||||
|
'102': 'lightgreen',
|
||||||
|
'103': 'lightyellow',
|
||||||
|
'104': 'lightblue',
|
||||||
|
'105': 'lightmagenta',
|
||||||
|
'106': 'lightcyan',
|
||||||
|
'107': 'white',
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles: Record<string, string> = {
|
||||||
|
'1': 'font-weight: bold;',
|
||||||
|
'3': 'font-style: italic;',
|
||||||
|
'4': 'text-decoration: underline;',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ESC = '\u001b';
|
||||||
|
const ansiPattern = new RegExp(`${ESC}\\[(\\d+(?:;\\d+)*)m`, 'g');
|
||||||
|
const openTags: string[] = [];
|
||||||
|
|
||||||
|
let result = text.replace(ansiPattern, (_, codes) => {
|
||||||
|
const codeArray = codes.split(';');
|
||||||
|
|
||||||
|
if (codeArray.includes('0')) {
|
||||||
|
const closeTags = openTags.length > 0 ? '</span>'.repeat(openTags.length) : '';
|
||||||
|
openTags.length = 0;
|
||||||
|
return closeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
let style = '';
|
||||||
|
|
||||||
|
for (const code of codeArray) {
|
||||||
|
if (textColors[code]) {
|
||||||
|
style += `color: ${textColors[code]};`;
|
||||||
|
} else if (bgColors[code]) {
|
||||||
|
style += `background-color: ${bgColors[code]};`;
|
||||||
|
} else if (styles[code]) {
|
||||||
|
style += styles[code];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style) {
|
||||||
|
openTags.push('span');
|
||||||
|
return `<span style="${style}">`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (openTags.length > 0) result += '</span>'.repeat(openTags.length);
|
||||||
|
|
||||||
|
return unsafeHTML(`${result}\n`);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue