Compare commits
19 commits
feature/wo
...
main
Author | SHA1 | Date | |
---|---|---|---|
91e60d2f4c | |||
732dd260ea | |||
d33ad7ee21 | |||
d3b5f41a3a | |||
033fe09829 | |||
0041ec3d9a | |||
48e585d4ec | |||
1fbddd0e8d | |||
6ae7523d63 | |||
468e105adf | |||
4057e18755 | |||
2ce53eb499 | |||
65b6df5d92 | |||
00b40c4d75 | |||
b9c08dec64 | |||
bd6e938335 | |||
1c43c3ef21 | |||
9843706066 | |||
|
a59de5796f |
35 changed files with 1928 additions and 1139 deletions
|
@ -1,10 +1,11 @@
|
|||
# BTClock WebUI
|
||||
|
||||
[![BTClock CI](https://github.com/btclock/webui/actions/workflows/workflow.yml/badge.svg)](https://github.com/btclock/webui2/actions/workflows/workflow.yml)
|
||||
[![Latest release](https://git.btclock.dev/btclock/webui/badges/release.svg)](https://git.btclock.dev/btclock/webui/releases/latest)
|
||||
[![BTClock CI](https://git.btclock.dev/btclock/webui/badges/workflows/build.yaml/badge.svg)](https://git.btclock.dev/btclock/webui/actions?workflow=build.yaml&actor=0&status=0)
|
||||
|
||||
The web user-interface for the BTClock, based on Svelte-kit. It uses Bootstrap for the lay-out.
|
||||
|
||||
![Screenshot](doc/screenshot.webp)
|
||||
![Screenshot](doc/screenshot-light.webp)
|
||||
![Screenshot Dark](doc/screenshot-dark.webp)
|
||||
|
||||
## Developing
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 70 KiB |
BIN
doc/screenshot-light.webp
Normal file
BIN
doc/screenshot-light.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
Binary file not shown.
Before Width: | Height: | Size: 53 KiB |
10
package.json
10
package.json
|
@ -5,15 +5,17 @@
|
|||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"build:test": "vite build --config vite.config.test.ts",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write .",
|
||||
"postinstall": "patch-package",
|
||||
"test": "npm run test:integration && npm run test:unit",
|
||||
"test": "prettier --write . && eslint . && npm run test:integration && npm run test:unit",
|
||||
"test:integration": "playwright test",
|
||||
"test:screenshots": "playwright test -c playwright.screenshot.config.ts",
|
||||
"doc:update-screenshots": "playwright test -c playwright.doc-screenshot.config.ts",
|
||||
"test:unit": "vitest"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -35,6 +37,7 @@
|
|||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"sass": "^1.79.3",
|
||||
"sharp": "^0.33.5",
|
||||
"svelte": "^4.2.19",
|
||||
"svelte-check": "^4.0.2",
|
||||
"svelte-preprocess": "^6.0.2",
|
||||
|
@ -42,8 +45,7 @@
|
|||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.7.0",
|
||||
"vite": "^5.4.7",
|
||||
"vitest": "^2.1.1",
|
||||
"vitest-github-actions-reporter": "^0.11.0"
|
||||
"vitest": "^2.1.1"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
@ -65,9 +67,7 @@
|
|||
},
|
||||
"resolutions": {
|
||||
"es5-ext": ">=0.10.64",
|
||||
"undici": ">=5.28.4",
|
||||
"ws": ">=8.18.0",
|
||||
"axios": ">=1.7.7",
|
||||
"micromatch": ">=4.0.8"
|
||||
},
|
||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
||||
|
|
|
@ -6,7 +6,7 @@ const config: PlaywrightTestConfig = {
|
|||
timezoneId: 'Europe/Amsterdam'
|
||||
},
|
||||
webServer: {
|
||||
command: 'npm run build && npm run preview',
|
||||
command: 'npm run build:test && npm run preview',
|
||||
port: 4173
|
||||
},
|
||||
reporter: process.env.CI ? 'github' : 'list',
|
||||
|
|
27
playwright.doc-screenshot.config.ts
Normal file
27
playwright.doc-screenshot.config.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
use: {
|
||||
locale: 'en-GB',
|
||||
timezoneId: 'Europe/Amsterdam'
|
||||
},
|
||||
webServer: {
|
||||
command: 'yarn build && yarn preview',
|
||||
port: 4173
|
||||
},
|
||||
testDir: './tests/doc-screenshots',
|
||||
outputDir: './test-results/screenshots',
|
||||
projects: [
|
||||
{
|
||||
name: 'Light Mode',
|
||||
use: {
|
||||
viewport: { width: 1440, height: 900 },
|
||||
colorScheme: 'light'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Dark Mode',
|
||||
use: { viewport: { width: 1440, height: 900 }, colorScheme: 'dark' }
|
||||
}
|
||||
]
|
||||
});
|
|
@ -54,6 +54,14 @@ export default defineConfig({
|
|||
{
|
||||
name: 'MacBook Pro 14 inch Safari',
|
||||
use: { ...devices['Desktop Safari'], viewport: { width: 1512, height: 982 } }
|
||||
},
|
||||
{
|
||||
name: 'MacBook Pro 14 inch Safari Dark Mode',
|
||||
use: {
|
||||
...devices['Desktop Safari'],
|
||||
viewport: { width: 1512, height: 982 },
|
||||
colorScheme: 'dark'
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:base"],
|
||||
"extends": ["config:recommended"],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackagePatterns": ["*"],
|
||||
"matchUpdateTypes": ["major"],
|
||||
"enabled": false
|
||||
"enabled": false,
|
||||
"matchPackageNames": ["*"]
|
||||
}
|
||||
],
|
||||
"npm": {
|
||||
|
|
11
src/lib/components/Placeholder.svelte
Normal file
11
src/lib/components/Placeholder.svelte
Normal file
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
export let value: unknown;
|
||||
export let checkValue: unknown = null;
|
||||
export let width: number = 25;
|
||||
|
||||
$: valueToCheck = checkValue === null ? value : checkValue;
|
||||
</script>
|
||||
|
||||
<span class:placeholder={!valueToCheck} class={!valueToCheck ? `w-${width}` : ''}>
|
||||
{valueToCheck ? value : ''}
|
||||
</span>
|
|
@ -3,3 +3,4 @@ export { default as SettingsInput } from './SettingsInput.svelte';
|
|||
export { default as SettingsSelect } from './SettingsSelect.svelte';
|
||||
export { default as ToggleHeader } from './ToggleHeader.svelte';
|
||||
export { default as ColorSchemeSwitcher } from './ColorSchemeSwitcher.svelte';
|
||||
export { default as Placeholder } from './Placeholder.svelte';
|
||||
|
|
142
src/lib/components/settings/DataSourceSettings.svelte
Normal file
142
src/lib/components/settings/DataSourceSettings.svelte
Normal file
|
@ -0,0 +1,142 @@
|
|||
<script lang="ts">
|
||||
import { SettingsInput, SettingsSwitch } from '$lib/components';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { Row, Col, FormGroup, Input, InputGroupText } from '@sveltestrap/sveltestrap';
|
||||
import ToggleHeader from '../ToggleHeader.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { isValidHexPubKey, getPubKey, isValidNpub } from '$lib';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { DataSourceType } from '$lib/types/dataSource';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
export let settings;
|
||||
export let isOpen = false;
|
||||
|
||||
const checkValidNostrPubkey = (key: string) => {
|
||||
$settings[key] = $settings[key].trim();
|
||||
if (isValidNpub($settings[key])) {
|
||||
dispatch('showToast', {
|
||||
color: 'info',
|
||||
text: $_('section.settings.convertingValidNpub')
|
||||
});
|
||||
}
|
||||
|
||||
let ret = getPubKey($settings[key]);
|
||||
if (ret) $settings[key] = ret;
|
||||
};
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
<ToggleHeader header={$_('section.settings.section.dataSource')} bind:isOpen defaultOpen={false}>
|
||||
<Row>
|
||||
<Col>
|
||||
<h5>Data Source</h5>
|
||||
<FormGroup>
|
||||
<Row>
|
||||
<Col xs="12" xl="6" class="mb-2">
|
||||
<Input
|
||||
type="radio"
|
||||
id="btclock_source"
|
||||
name="dataSource"
|
||||
bind:group={$settings.dataSource}
|
||||
value={DataSourceType.BTCLOCK_SOURCE}
|
||||
label={$_('section.settings.dataSource.btclock')}
|
||||
/>
|
||||
</Col>
|
||||
<Col xs="12" xl="6" class="mb-2">
|
||||
<Input
|
||||
type="radio"
|
||||
id="third_party_source"
|
||||
name="dataSource"
|
||||
bind:group={$settings.dataSource}
|
||||
value={DataSourceType.THIRD_PARTY_SOURCE}
|
||||
label={$_('section.settings.dataSource.thirdParty')}
|
||||
/>
|
||||
</Col>
|
||||
{#if $settings.nostrRelay}
|
||||
<Col xs="12" xl="6" class="mb-2">
|
||||
<Input
|
||||
type="radio"
|
||||
id="nostr_source"
|
||||
name="dataSource"
|
||||
bind:group={$settings.dataSource}
|
||||
value={DataSourceType.NOSTR_SOURCE}
|
||||
label={$_('section.settings.dataSource.nostr')}
|
||||
/>
|
||||
</Col>
|
||||
{/if}
|
||||
<Col xs="12" xl="6" class="mb-2">
|
||||
<Input
|
||||
type="radio"
|
||||
id="custom_source"
|
||||
name="dataSource"
|
||||
bind:group={$settings.dataSource}
|
||||
value={DataSourceType.CUSTOM_SOURCE}
|
||||
label={$_('section.settings.dataSource.custom')}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{#if $settings.dataSource === DataSourceType.THIRD_PARTY_SOURCE}
|
||||
<SettingsInput
|
||||
id="mempoolInstance"
|
||||
label={$_('section.settings.mempoolnstance')}
|
||||
bind:value={$settings.mempoolInstance}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<InputGroupText>
|
||||
<Input
|
||||
type="checkbox"
|
||||
bind:checked={$settings.mempoolSecure}
|
||||
bsSize={$uiSettings.inputSize}
|
||||
/>
|
||||
HTTPS
|
||||
</InputGroupText>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
|
||||
{#if $settings.dataSource === DataSourceType.NOSTR_SOURCE}
|
||||
<SettingsInput
|
||||
id="nostrRelay"
|
||||
label={$_('section.settings.nostrRelay')}
|
||||
bind:value={$settings.nostrRelay}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsInput
|
||||
id="nostrPubKey"
|
||||
label={$_('section.settings.nostrPubKey')}
|
||||
bind:value={$settings.nostrPubKey}
|
||||
required={true}
|
||||
minlength="64"
|
||||
invalid={!isValidHexPubKey($settings.nostrPubKey)}
|
||||
helpText={!isValidHexPubKey($settings.nostrPubKey)
|
||||
? $_('section.settings.invalidNostrPubkey')
|
||||
: undefined}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={() => checkValidNostrPubkey('nostrPubKey')}
|
||||
onInput={() => checkValidNostrPubkey('nostrPubKey')}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $settings.dataSource === DataSourceType.CUSTOM_SOURCE}
|
||||
<SettingsInput
|
||||
id="ceEndpoint"
|
||||
label={$_('section.settings.ceEndpoint')}
|
||||
bind:value={$settings.ceEndpoint}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="ceDisableSSL"
|
||||
bind:checked={$settings.ceDisableSSL}
|
||||
label={$_('section.settings.ceDisableSSL')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</ToggleHeader>
|
||||
</Row>
|
202
src/lib/components/settings/DisplaySettings.svelte
Normal file
202
src/lib/components/settings/DisplaySettings.svelte
Normal file
|
@ -0,0 +1,202 @@
|
|||
<script lang="ts">
|
||||
import { SettingsInput, SettingsSwitch, SettingsSelect } from '$lib/components';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { Row } from '@sveltestrap/sveltestrap';
|
||||
import ToggleHeader from '../ToggleHeader.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
||||
|
||||
export let settings;
|
||||
export let isOpen = false;
|
||||
|
||||
const onFlBrightnessChange = async () => {
|
||||
await fetch(`${PUBLIC_BASE_URL}/api/frontlight/brightness/${$settings.flMaxBrightness}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setTextColor = () => {
|
||||
$settings.invertedColor = !$settings.invertedColor;
|
||||
};
|
||||
|
||||
const textColorOptions: [string, boolean][] = [
|
||||
[$_('colors.black') + ' on ' + $_('colors.white'), false],
|
||||
[$_('colors.white') + ' on ' + $_('colors.black'), true]
|
||||
];
|
||||
|
||||
const fontPreferenceOptions: [string, string][] = $settings.availableFonts?.map((font) => [
|
||||
$_(`fonts.${font}`) !== `fonts.${font}`
|
||||
? $_(`fonts.${font}`)
|
||||
: font.charAt(0).toUpperCase() + font.slice(1),
|
||||
font
|
||||
]);
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.displaysAndLed')}
|
||||
bind:isOpen
|
||||
defaultOpen={false}
|
||||
>
|
||||
<SettingsSelect
|
||||
id="textColor"
|
||||
label={$_('section.settings.textColor')}
|
||||
bind:value={$settings.invertedColor}
|
||||
options={textColorOptions}
|
||||
size={$uiSettings.inputSize}
|
||||
on:change={setTextColor}
|
||||
/>
|
||||
|
||||
<SettingsSelect
|
||||
id="fontName"
|
||||
label={$_('section.settings.fontName')}
|
||||
bind:value={$settings.fontName}
|
||||
options={fontPreferenceOptions}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="timePerScreen"
|
||||
label={$_('section.settings.timePerScreen')}
|
||||
bind:value={$settings.timePerScreen}
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="fullRefreshMin"
|
||||
label={$_('section.settings.fullRefreshEvery')}
|
||||
bind:value={$settings.fullRefreshMin}
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="minSecPriceUpd"
|
||||
label={$_('section.settings.timeBetweenPriceUpdates')}
|
||||
bind:value={$settings.minSecPriceUpd}
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
suffix={$_('time.seconds')}
|
||||
helpText={$_('section.settings.shortAmountsWarning')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="ledBrightness"
|
||||
label={$_('section.settings.ledBrightness')}
|
||||
bind:value={$settings.ledBrightness}
|
||||
type="range"
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsInput
|
||||
id="flMaxBrightness"
|
||||
label={$_('section.settings.flMaxBrightness')}
|
||||
bind:value={$settings.flMaxBrightness}
|
||||
type="range"
|
||||
min={0}
|
||||
max={4095}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={onFlBrightnessChange}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="flEffectDelay"
|
||||
label={$_('section.settings.flEffectDelay')}
|
||||
bind:value={$settings.flEffectDelay}
|
||||
type="range"
|
||||
min={5}
|
||||
max={300}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !$settings.flDisable && $settings.hasLightLevel}
|
||||
<SettingsInput
|
||||
id="luxLightToggle"
|
||||
label={`${$_('section.settings.luxLightToggle')} (${$settings.luxLightToggle})`}
|
||||
bind:value={$settings.luxLightToggle}
|
||||
type="range"
|
||||
min={0}
|
||||
max={1000}
|
||||
step={1}
|
||||
helpText={$_('section.settings.luxLightToggleText')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="ledTestOnPower"
|
||||
bind:checked={$settings.ledTestOnPower}
|
||||
label={$_('section.settings.ledPowerOnTest')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="ledFlashOnUpd"
|
||||
bind:checked={$settings.ledFlashOnUpd}
|
||||
label={$_('section.settings.ledFlashOnBlock')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="disableLeds"
|
||||
bind:checked={$settings.disableLeds}
|
||||
label={$_('section.settings.disableLeds')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
{#if $settings.hasFrontlight}
|
||||
<SettingsSwitch
|
||||
id="flDisable"
|
||||
bind:checked={$settings.flDisable}
|
||||
label={$_('section.settings.flDisable')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsSwitch
|
||||
id="flAlwaysOn"
|
||||
bind:checked={$settings.flAlwaysOn}
|
||||
label={$_('section.settings.flAlwaysOn')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="flFlashOnUpd"
|
||||
bind:checked={$settings.flFlashOnUpd}
|
||||
label={$_('section.settings.flFlashOnUpd')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="flOffWhenDark"
|
||||
bind:checked={$settings.flOffWhenDark}
|
||||
label={$_('section.settings.flOffWhenDark')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row>
|
248
src/lib/components/settings/ExtraFeaturesSettings.svelte
Normal file
248
src/lib/components/settings/ExtraFeaturesSettings.svelte
Normal file
|
@ -0,0 +1,248 @@
|
|||
<script lang="ts">
|
||||
import { SettingsInput, SettingsSwitch, SettingsSelect } from '$lib/components';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { Row, Button, Col } from '@sveltestrap/sveltestrap';
|
||||
import ToggleHeader from '../ToggleHeader.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { isValidHexPubKey, getPubKey, isValidNpub } from '$lib';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
export let settings;
|
||||
export let isOpen = false;
|
||||
export let miningPoolMap: Map<string, string>;
|
||||
|
||||
let validBitaxe = false;
|
||||
const testBitaxe = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://${$settings.bitaxeHostname}/api/system/info`);
|
||||
|
||||
if (!response.ok) {
|
||||
dispatch('showToast', {
|
||||
color: 'danger',
|
||||
text: `Failed to connect to BitAxe HTTP error! status: ${response.status}`
|
||||
});
|
||||
validBitaxe = false;
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const systemInfo = await response.json();
|
||||
dispatch('showToast', {
|
||||
color: 'success',
|
||||
text: `Connected to BitAxe ${systemInfo.ASICModel} (Board version ${systemInfo.boardVersion}) running firmware ${systemInfo.version}.\r\nCurrent hashrate ${Math.round(systemInfo.hashRate)} GH/s`
|
||||
});
|
||||
validBitaxe = true;
|
||||
} catch (error) {
|
||||
if (error instanceof TypeError && error.message.includes('Failed to fetch')) {
|
||||
dispatch('showToast', {
|
||||
color: 'danger',
|
||||
text: `Failed to connect to BitAxe, make sure you are connected to the same network.`
|
||||
});
|
||||
}
|
||||
console.error('Failed to fetch Bitaxe system info:', error);
|
||||
validBitaxe = false;
|
||||
}
|
||||
};
|
||||
|
||||
const checkValidNostrPubkey = (key: string) => {
|
||||
$settings[key] = $settings[key].trim();
|
||||
if (isValidNpub($settings[key])) {
|
||||
dispatch('showToast', {
|
||||
color: 'info',
|
||||
text: $_('section.settings.convertingValidNpub')
|
||||
});
|
||||
}
|
||||
|
||||
let ret = getPubKey($settings[key]);
|
||||
if (ret) $settings[key] = ret;
|
||||
};
|
||||
|
||||
$: poolOptions = ($settings.availablePools || []).map((pool: string): [string, string] => [
|
||||
miningPoolMap.get(pool) || pool,
|
||||
pool
|
||||
]);
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.extraFeatures')}
|
||||
bind:isOpen
|
||||
defaultOpen={false}
|
||||
>
|
||||
<!--- Time based do not disturb settings -->
|
||||
<SettingsSwitch
|
||||
id="timeBasedDnd"
|
||||
label={$_('section.settings.timeBasedDnd')}
|
||||
bind:checked={$settings.dnd.timeBasedEnabled}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{#if $settings.dnd.timeBasedEnabled}
|
||||
<Row>
|
||||
<Col>
|
||||
<SettingsInput
|
||||
id="dndStartHour"
|
||||
type="number"
|
||||
min="0"
|
||||
max="23"
|
||||
label={$_('section.settings.dndStartHour')}
|
||||
bind:value={$settings.dnd.startHour}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<SettingsInput
|
||||
id="dndStartMinute"
|
||||
type="number"
|
||||
min="0"
|
||||
max="59"
|
||||
label={$_('section.settings.dndStartMinute')}
|
||||
bind:value={$settings.dnd.startMinute}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
<SettingsInput
|
||||
id="dndEndHour"
|
||||
type="number"
|
||||
min="0"
|
||||
max="23"
|
||||
label={$_('section.settings.dndEndHour')}
|
||||
bind:value={$settings.dnd.endHour}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Col>
|
||||
<Col>
|
||||
<SettingsInput
|
||||
id="dndEndMinute"
|
||||
type="number"
|
||||
min="0"
|
||||
max="59"
|
||||
label={$_('section.settings.dndEndMinute')}
|
||||
bind:value={$settings.dnd.endMinute}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{/if}
|
||||
|
||||
<!-- BitAxe Settings -->
|
||||
{#if 'bitaxeEnabled' in $settings}
|
||||
<Row class="mb-3">
|
||||
<Col>
|
||||
<h5>BitAxe</h5>
|
||||
<SettingsSwitch
|
||||
id="bitaxeEnabled"
|
||||
bind:checked={$settings.bitaxeEnabled}
|
||||
label="{$_('section.settings.bitaxeEnabled')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
||||
/>
|
||||
{#if $settings.bitaxeEnabled}
|
||||
<SettingsInput
|
||||
id="bitaxeHostname"
|
||||
label={$_('section.settings.bitaxeHostname')}
|
||||
bind:value={$settings.bitaxeHostname}
|
||||
required={true}
|
||||
valid={validBitaxe}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button type="button" color="success" on:click={testBitaxe}>
|
||||
{$_('test', { default: 'Test' })}
|
||||
</Button>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
</Col>
|
||||
</Row>
|
||||
{/if}
|
||||
|
||||
<!-- Mining Pool Settings -->
|
||||
{#if 'miningPoolStats' in $settings}
|
||||
<Row class="mb-3">
|
||||
<Col>
|
||||
<h5>Mining Pool stats</h5>
|
||||
<SettingsSwitch
|
||||
id="miningPoolStats"
|
||||
bind:checked={$settings.miningPoolStats}
|
||||
label="{$_('section.settings.miningPoolStats')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
||||
/>
|
||||
{#if $settings.miningPoolStats}
|
||||
<SettingsSelect
|
||||
id="miningPoolName"
|
||||
label={$_('section.settings.miningPoolName')}
|
||||
bind:value={$settings.miningPoolName}
|
||||
options={poolOptions}
|
||||
size={$uiSettings.inputSize}
|
||||
selectClass={$uiSettings.selectClass}
|
||||
/>
|
||||
<SettingsInput
|
||||
id="miningPoolUser"
|
||||
label={$_('section.settings.miningPoolUser')}
|
||||
bind:value={$settings.miningPoolUser}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</Col>
|
||||
</Row>
|
||||
{/if}
|
||||
|
||||
<!-- Nostr Settings -->
|
||||
{#if 'nostrZapNotify' in $settings}
|
||||
<Row class="mb-3">
|
||||
<Col>
|
||||
<h5>Nostr</h5>
|
||||
<SettingsInput
|
||||
id="nostrRelay"
|
||||
label={$_('section.settings.nostrRelay')}
|
||||
bind:value={$settings.nostrRelay}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="nostrZapNotify"
|
||||
bind:checked={$settings.nostrZapNotify}
|
||||
label="{$_('section.settings.nostrZapNotify')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '12', xl: '12', xxl: '12' }}
|
||||
/>
|
||||
{#if $settings.nostrZapNotify}
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="ledFlashOnZap"
|
||||
bind:checked={$settings.ledFlashOnZap}
|
||||
label={$_('section.settings.ledFlashOnZap')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsSwitch
|
||||
id="flFlashOnZap"
|
||||
bind:checked={$settings.flFlashOnZap}
|
||||
label={$_('section.settings.flFlashOnZap')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
<SettingsInput
|
||||
id="nostrZapPubkey"
|
||||
label={$_('section.settings.nostrZapPubkey')}
|
||||
bind:value={$settings.nostrZapPubkey}
|
||||
required={true}
|
||||
minlength="64"
|
||||
invalid={!isValidHexPubKey($settings.nostrZapPubkey)}
|
||||
helpText={!isValidHexPubKey($settings.nostrZapPubkey)
|
||||
? $_('section.settings.invalidNostrPubkey')
|
||||
: undefined}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={() => checkValidNostrPubkey('nostrZapPubkey')}
|
||||
onInput={() => checkValidNostrPubkey('nostrZapPubkey')}
|
||||
/>
|
||||
{/if}
|
||||
</Col>
|
||||
</Row>
|
||||
{/if}
|
||||
</ToggleHeader>
|
||||
</Row>
|
126
src/lib/components/settings/ScreenSpecificSettings.svelte
Normal file
126
src/lib/components/settings/ScreenSpecificSettings.svelte
Normal file
|
@ -0,0 +1,126 @@
|
|||
<script lang="ts">
|
||||
import { SettingsSwitch } from '$lib/components';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { Row, Col } from '@sveltestrap/sveltestrap';
|
||||
import ToggleHeader from '../ToggleHeader.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { DataSourceType } from '$lib/types/dataSource';
|
||||
|
||||
export let settings;
|
||||
export let isOpen = false;
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.screenSettings')}
|
||||
bind:isOpen
|
||||
defaultOpen={true}
|
||||
>
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="stealFocus"
|
||||
bind:checked={$settings.stealFocus}
|
||||
label={$_('section.settings.StealFocusOnNewBlock')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mcapBigChar"
|
||||
bind:checked={$settings.mcapBigChar}
|
||||
label={$_('section.settings.useBigCharsMcap')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="useBlkCountdown"
|
||||
bind:checked={$settings.useBlkCountdown}
|
||||
label={$_('section.settings.useBlkCountdown')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="useSatsSymbol"
|
||||
bind:checked={$settings.useSatsSymbol}
|
||||
label={$_('section.settings.useSatsSymbol')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="suffixPrice"
|
||||
bind:checked={$settings.suffixPrice}
|
||||
label={$_('section.settings.suffixPrice')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mowMode"
|
||||
bind:checked={$settings.mowMode}
|
||||
label={$_('section.settings.mowMode')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
disabled={!$settings.suffixPrice}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="suffixShareDot"
|
||||
bind:checked={$settings.suffixShareDot}
|
||||
label={$_('section.settings.suffixShareDot')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
disabled={!$settings.suffixPrice}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="verticalDesc"
|
||||
bind:checked={$settings.verticalDesc}
|
||||
label={$_('section.settings.verticalDesc')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
|
||||
{#if !$settings.actCurrencies}
|
||||
<SettingsSwitch
|
||||
id="fetchEurPrice"
|
||||
bind:checked={$settings.fetchEurPrice}
|
||||
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
<Row>
|
||||
<h5>{$_('section.settings.screens')}</h5>
|
||||
{#if $settings.screens}
|
||||
{#each $settings.screens as s}
|
||||
<SettingsSwitch
|
||||
id="screens_{s.id}"
|
||||
bind:checked={s.enabled}
|
||||
label={s.name}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
{#if $settings.actCurrencies && $settings.dataSource == DataSourceType.BTCLOCK_SOURCE}
|
||||
<Row>
|
||||
<h5>{$_('section.settings.currencies')}</h5>
|
||||
<small>{$_('restartRequired')}</small>
|
||||
{#if $settings.availableCurrencies}
|
||||
{#each $settings.availableCurrencies as c}
|
||||
<Col md="6" xl="12" xxl="6">
|
||||
<div class="form-check form-control-{$uiSettings.inputSize}">
|
||||
<input
|
||||
id="currency_{c}"
|
||||
bind:group={$settings.actCurrencies}
|
||||
value={c}
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
/>
|
||||
<label class="form-check-label" for="currency_{c}">{c}</label>
|
||||
</div>
|
||||
</Col>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
{/if}
|
||||
</ToggleHeader>
|
||||
</Row>
|
113
src/lib/components/settings/SystemSettings.svelte
Normal file
113
src/lib/components/settings/SystemSettings.svelte
Normal file
|
@ -0,0 +1,113 @@
|
|||
<script lang="ts">
|
||||
import { SettingsInput, SettingsSwitch } from '$lib/components';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { Row, Button } from '@sveltestrap/sveltestrap';
|
||||
import ToggleHeader from '../ToggleHeader.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import EyeIcon from 'svelte-bootstrap-icons/lib/Eye.svelte';
|
||||
import EyeSlashIcon from 'svelte-bootstrap-icons/lib/EyeSlash.svelte';
|
||||
|
||||
export let settings;
|
||||
export let isOpen = false;
|
||||
|
||||
let showPassword = false;
|
||||
|
||||
const getTzOffsetFromSystem = () => {
|
||||
const dt = new Date();
|
||||
let diffTZ = dt.getTimezoneOffset();
|
||||
$settings.tzOffset = diffTZ * -1;
|
||||
};
|
||||
</script>
|
||||
|
||||
<Row>
|
||||
<ToggleHeader header={$_('section.settings.section.system')} bind:isOpen defaultOpen={false}>
|
||||
<SettingsInput
|
||||
id="tzOffset"
|
||||
label={$_('section.settings.timezoneOffset')}
|
||||
bind:value={$settings.tzOffset}
|
||||
type="number"
|
||||
step={1}
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
helpText={$_('section.settings.tzOffsetHelpText')}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button type="button" color="info" on:click={getTzOffsetFromSystem}>
|
||||
{$_('auto-detect')}
|
||||
</Button>
|
||||
</SettingsInput>
|
||||
|
||||
{#if $settings.httpAuthEnabled}
|
||||
<SettingsInput
|
||||
id="httpAuthUser"
|
||||
label={$_('section.settings.httpAuthUser')}
|
||||
bind:value={$settings.httpAuthUser}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsInput
|
||||
id="httpAuthPass"
|
||||
label={$_('section.settings.httpAuthPass')}
|
||||
bind:value={$settings.httpAuthPass}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
on:click={() => (showPassword = !showPassword)}
|
||||
color={showPassword ? 'success' : 'danger'}
|
||||
>
|
||||
{#if !showPassword}<EyeIcon />{:else}<EyeSlashIcon />{/if}
|
||||
</Button>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
|
||||
<SettingsInput
|
||||
id="hostnamePrefix"
|
||||
label={$_('section.settings.hostnamePrefix')}
|
||||
bind:value={$settings.hostnamePrefix}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="wpTimeout"
|
||||
label={$_('section.settings.wpTimeout')}
|
||||
bind:value={$settings.wpTimeout}
|
||||
type="number"
|
||||
min={1}
|
||||
step={1}
|
||||
required={true}
|
||||
suffix={$_('time.seconds')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="otaEnabled"
|
||||
bind:checked={$settings.otaEnabled}
|
||||
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mdnsEnabled"
|
||||
bind:checked={$settings.mdnsEnabled}
|
||||
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="httpAuthEnabled"
|
||||
bind:checked={$settings.httpAuthEnabled}
|
||||
label="{$_('section.settings.httpAuthEnabled')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="enableDebugLog"
|
||||
bind:checked={$settings.enableDebugLog}
|
||||
label="{$_('section.settings.enableDebugLog')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row>
|
5
src/lib/components/settings/index.ts
Normal file
5
src/lib/components/settings/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export { default as ScreenSpecificSettings } from './ScreenSpecificSettings.svelte';
|
||||
export { default as DisplaySettings } from './DisplaySettings.svelte';
|
||||
export { default as DataSourceSettings } from './DataSourceSettings.svelte';
|
||||
export { default as ExtraFeaturesSettings } from './ExtraFeaturesSettings.svelte';
|
||||
export { default as SystemSettings } from './SystemSettings.svelte';
|
0
src/lib/i18n/en.json
Normal file
0
src/lib/i18n/en.json
Normal file
|
@ -30,7 +30,7 @@
|
|||
"wifiTxPower": "WiFi-TX-Leistung",
|
||||
"settingsSaved": "Einstellungen gespeichert",
|
||||
"errorSavingSettings": "Fehler beim Speichern der Einstellungen",
|
||||
"ownDataSource": "BTClock-Datenquelle verwenden",
|
||||
"ownDataSource": "BTClock-Datenquelle",
|
||||
"flAlwaysOn": "Displaybeleuchtung immer an",
|
||||
"flEffectDelay": "Displaybeleuchtungeffekt Geschwindigkeit",
|
||||
"flFlashOnUpd": "Displaybeleuchting bei neuem Block",
|
||||
|
@ -58,7 +58,22 @@
|
|||
"hideAll": "Alles ausblenden",
|
||||
"flOffWhenDark": "Displaybeleuchtung aus, wenn es dunkel ist",
|
||||
"luxLightToggleText": "Zum Deaktivieren auf 0 setzen",
|
||||
"verticalDesc": "Vrtikale Bildschirmbeschreibung"
|
||||
"verticalDesc": "Vrtikale Bildschirmbeschreibung",
|
||||
"enableDebugLog": "Debug-Protokoll aktivieren",
|
||||
"bitaxeEnabled": "BitAxe-Integration aktivieren",
|
||||
"miningPoolStats": "Mining-Pool-Statistiken Integration Aktivieren",
|
||||
"nostrZapNotify": "Nostr Zap-Benachrichtigungen aktivieren",
|
||||
"thirdPartySource": "mempool.space/coincap.io Verwenden",
|
||||
"dataSource": {
|
||||
"nostr": "Nostr-Verlag",
|
||||
"custom": "Benutzerdefinierter dataquelle"
|
||||
},
|
||||
"fontName": "Schriftart",
|
||||
"timeBasedDnd": "Aktivieren Sie den Zeitplan „Bitte nicht stören“.",
|
||||
"dndStartHour": "Startstunde",
|
||||
"dndStartMinute": "Startminute",
|
||||
"dndEndHour": "Endstunde",
|
||||
"dndEndMinute": "Schlussminute"
|
||||
},
|
||||
"control": {
|
||||
"systemInfo": "Systeminfo",
|
||||
|
@ -86,7 +101,9 @@
|
|||
"wifiSignalStrength": "WiFi-Signalstärke",
|
||||
"wsDataConnection": "BTClock-Datenquelle verbindung",
|
||||
"lightSensor": "Lichtsensor",
|
||||
"nostrConnection": "Nostr Relay-Verbindung"
|
||||
"nostrConnection": "Nostr Relay-Verbindung",
|
||||
"doNotDisturb": "Bitte nicht stören",
|
||||
"timeBasedDnd": "Zeitbasierter Zeitplan"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Datei erfolgreich hochgeladen, Gerät neu gestartet. WebUI in {countdown} Sekunden neu geladen",
|
||||
|
@ -98,7 +115,8 @@
|
|||
"latestVersion": "Letzte Version",
|
||||
"releaseDate": "Veröffentlichungsdatum",
|
||||
"viewRelease": "Veröffentlichung anzeigen",
|
||||
"autoUpdate": "Update installieren (experimentell)"
|
||||
"autoUpdate": "Update installieren (experimentell)",
|
||||
"autoUpdateInProgress": "Automatische Aktualisierung läuft, bitte warten..."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"wifiTxPower": "WiFi TX power",
|
||||
"settingsSaved": "Settings saved",
|
||||
"errorSavingSettings": "Error saving settings",
|
||||
"ownDataSource": "Use BTClock data source",
|
||||
"ownDataSource": "BTClock data source",
|
||||
"flMaxBrightness": "Frontlight brightness",
|
||||
"flAlwaysOn": "Frontlight always on",
|
||||
"flEffectDelay": "Frontlight effect speed",
|
||||
|
@ -40,11 +40,11 @@
|
|||
"nostrPubKey": "Nostr source pubkey",
|
||||
"nostrZapKey": "Nostr zap pubkey",
|
||||
"nostrRelay": "Nostr Relay",
|
||||
"nostrZapNotify": "Nostr Zap Notifications",
|
||||
"nostrZapNotify": "Enable Nostr Zap Notifications",
|
||||
"useNostr": "Use Nostr data source",
|
||||
"bitaxeHostname": "BitAxe hostname or IP",
|
||||
"bitaxeEnabled": "Enable BitAxe",
|
||||
"miningPoolStats": "Enable Mining Pool Stats",
|
||||
"bitaxeEnabled": "Enable BitAxe-integration",
|
||||
"miningPoolStats": "Enable Mining Pool Stats integration",
|
||||
"miningPoolName": "Mining Pool",
|
||||
"miningPoolUser": "Mining Pool username or api key",
|
||||
"nostrZapPubkey": "Nostr Zap pubkey",
|
||||
|
@ -56,7 +56,7 @@
|
|||
"httpAuthPass": "WebUI Password",
|
||||
"httpAuthText": "Only password-protects WebUI, not API-calls.",
|
||||
"currencies": "Currencies",
|
||||
"stagingSource": "Use Staging data source (for development)",
|
||||
"customSource": "Use custom data source endpoint",
|
||||
"useNostrTooltip": "Very experimental and unstable. Nostr data source is not required for Nostr Zap notifications.",
|
||||
"mowMode": "Mow Suffix Mode",
|
||||
"suffixShareDot": "Suffix compact notation",
|
||||
|
@ -73,7 +73,24 @@
|
|||
"hideAll": "Hide all",
|
||||
"flOffWhenDark": "Frontlight off when dark",
|
||||
"luxLightToggleText": "Set to 0 to disable",
|
||||
"verticalDesc": "Use vertical screen description"
|
||||
"verticalDesc": "Use vertical screen description",
|
||||
"enableDebugLog": "Enable Debug-log",
|
||||
"dataSource": {
|
||||
"label": "Data Source",
|
||||
"btclock": "BTClock Data Source",
|
||||
"thirdParty": "mempool.space/coincap.io",
|
||||
"nostr": "Nostr publisher",
|
||||
"custom": "Custom Endpoint"
|
||||
},
|
||||
"thirdPartySource": "Use mempool.space/coincap.io",
|
||||
"ceDisableSSL": "Disable SSL",
|
||||
"ceEndpoint": "Endpoint hostname",
|
||||
"fontName": "Font",
|
||||
"timeBasedDnd": "Enable Do Not Disturb time schedule",
|
||||
"dndStartHour": "Start hour",
|
||||
"dndStartMinute": "Start minute",
|
||||
"dndEndHour": "End hour",
|
||||
"dndEndMinute": "End minute"
|
||||
},
|
||||
"control": {
|
||||
"systemInfo": "System info",
|
||||
|
@ -103,7 +120,9 @@
|
|||
"wifiSignalStrength": "WiFi Signal strength",
|
||||
"wsDataConnection": "BTClock data-source connection",
|
||||
"lightSensor": "Light sensor",
|
||||
"nostrConnection": "Nostr Relay connection"
|
||||
"nostrConnection": "Nostr Relay connection",
|
||||
"doNotDisturb": "Do not disturb",
|
||||
"timeBasedDnd": "Time-based schedule"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadFailed": "File upload failed. Make sure you have selected the correct file and try again.",
|
||||
|
@ -115,7 +134,8 @@
|
|||
"latestVersion": "Latest Version",
|
||||
"releaseDate": "Release Date",
|
||||
"viewRelease": "View Release",
|
||||
"autoUpdate": "Install update (experimental)"
|
||||
"autoUpdate": "Install update (experimental)",
|
||||
"autoUpdateInProgress": "Auto-update in progress, please wait..."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"wifiTxPowerText": "En la mayoría de los casos no es necesario configurar esto.",
|
||||
"settingsSaved": "Configuración guardada",
|
||||
"errorSavingSettings": "Error al guardar la configuración",
|
||||
"ownDataSource": "Utilice la fuente de datos BTClock",
|
||||
"ownDataSource": "fuente de datos BTClock",
|
||||
"flMaxBrightness": "Brillo de luz de la pantalla",
|
||||
"flAlwaysOn": "Luz de la pantalla siempre encendida",
|
||||
"flEffectDelay": "Velocidad del efecto de luz de la pantalla",
|
||||
|
@ -57,7 +57,22 @@
|
|||
"hideAll": "Ocultar todo",
|
||||
"flOffWhenDark": "Luz de la pantalla cuando está oscuro",
|
||||
"luxLightToggleText": "Establecer en 0 para desactivar",
|
||||
"verticalDesc": "Descripción de pantalla vertical"
|
||||
"verticalDesc": "Descripción de pantalla vertical",
|
||||
"enableDebugLog": "Habilitar registro de depuración",
|
||||
"bitaxeEnabled": "Habilitar la integración de BitAxe",
|
||||
"miningPoolStats": "Habilitar la integración de estadísticas del grupo minero",
|
||||
"nostrZapNotify": "Habilitar notificaciones de Nostr Zap",
|
||||
"thirdPartySource": "Utilice mempool.space/coincap.io",
|
||||
"dataSource": {
|
||||
"nostr": "editorial nostr",
|
||||
"custom": "Punto final personalizado"
|
||||
},
|
||||
"fontName": "Fuente",
|
||||
"timeBasedDnd": "Habilitar el horario de No molestar",
|
||||
"dndStartHour": "Hora de inicio",
|
||||
"dndStartMinute": "Minuto de inicio",
|
||||
"dndEndHour": "Hora final",
|
||||
"dndEndMinute": "Minuto final"
|
||||
},
|
||||
"control": {
|
||||
"turnOff": "Apagar",
|
||||
|
@ -85,7 +100,9 @@
|
|||
"wifiSignalStrength": "Fuerza de la señal WiFi",
|
||||
"wsDataConnection": "Conexión de fuente de datos BTClock",
|
||||
"lightSensor": "Sensor de luz",
|
||||
"nostrConnection": "Conexión de relé Nostr"
|
||||
"nostrConnection": "Conexión de relé Nostr",
|
||||
"doNotDisturb": "No molestar",
|
||||
"timeBasedDnd": "Horario basado en el tiempo"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Archivo cargado exitosamente, reiniciando el dispositivo. Recargando WebUI en {countdown} segundos",
|
||||
|
@ -97,7 +114,8 @@
|
|||
"latestVersion": "Ultima versión",
|
||||
"releaseDate": "Fecha de lanzamiento",
|
||||
"viewRelease": "Ver lanzamiento",
|
||||
"autoUpdate": "Instalar actualización (experimental)"
|
||||
"autoUpdate": "Instalar actualización (experimental)",
|
||||
"autoUpdateInProgress": "Actualización automática en progreso, espere..."
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
|
|
|
@ -58,7 +58,13 @@
|
|||
"hideAll": "Alles verbergen",
|
||||
"flOffWhenDark": "Displaylicht uit als het donker is",
|
||||
"luxLightToggleText": "Stel in op 0 om uit te schakelen",
|
||||
"verticalDesc": "Verticale schermbeschrijving"
|
||||
"verticalDesc": "Verticale schermbeschrijving",
|
||||
"fontName": "Lettertype",
|
||||
"timeBasedDnd": "Schakel het tijdschema Niet storen in",
|
||||
"dndStartHour": "Begin uur",
|
||||
"dndStartMinute": "Beginminuut",
|
||||
"dndEndHour": "Eind uur",
|
||||
"dndEndMinute": "Einde minuut"
|
||||
},
|
||||
"control": {
|
||||
"systemInfo": "Systeeminformatie",
|
||||
|
@ -85,7 +91,9 @@
|
|||
"wifiSignalStrength": "WiFi signaalsterkte",
|
||||
"wsDataConnection": "BTClock-gegevensbron verbinding",
|
||||
"lightSensor": "Licht sensor",
|
||||
"nostrConnection": "Nostr Relay-verbinding"
|
||||
"nostrConnection": "Nostr Relay-verbinding",
|
||||
"doNotDisturb": "Niet storen",
|
||||
"timeBasedDnd": "Op tijd gebaseerd schema"
|
||||
},
|
||||
"firmwareUpdater": {
|
||||
"fileUploadSuccess": "Bestand geüpload, apparaat herstart. WebUI opnieuw geladen over {countdown} seconden",
|
||||
|
@ -97,7 +105,8 @@
|
|||
"latestVersion": "Laatste versie",
|
||||
"releaseDate": "Datum van publicatie",
|
||||
"viewRelease": "Bekijk publicatie",
|
||||
"autoUpdate": "Update installeren (experimenteel)"
|
||||
"autoUpdate": "Update installeren (experimenteel)",
|
||||
"autoUpdateInProgress": "Automatische update wordt uitgevoerd. Even geduld a.u.b...."
|
||||
}
|
||||
},
|
||||
"colors": {
|
||||
|
|
|
@ -53,6 +53,8 @@ $input-font-size-sm: $font-size-base * 0.875;
|
|||
@import '../node_modules/bootstrap/scss/tooltip';
|
||||
@import '../node_modules/bootstrap/scss/toasts';
|
||||
@import '../node_modules/bootstrap/scss/alert';
|
||||
@import '../node_modules/bootstrap/scss/placeholders';
|
||||
@import '../node_modules/bootstrap/scss/spinners';
|
||||
|
||||
@import '../node_modules/bootstrap/scss/helpers';
|
||||
@import '../node_modules/bootstrap/scss/utilities/api';
|
||||
|
@ -317,3 +319,34 @@ input[type='number'] {
|
|||
.lightMode .bitaxelogo {
|
||||
filter: brightness(0) saturate(100%);
|
||||
}
|
||||
|
||||
.connection-lost-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.75);
|
||||
z-index: 1050;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.overlay-content {
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.5rem;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
font-size: 1rem;
|
||||
color: $danger;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
src/lib/types/dataSource.ts
Normal file
6
src/lib/types/dataSource.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export enum DataSourceType {
|
||||
BTCLOCK_SOURCE = 0,
|
||||
THIRD_PARTY_SOURCE = 1,
|
||||
NOSTR_SOURCE = 2,
|
||||
CUSTOM_SOURCE = 3
|
||||
}
|
|
@ -14,7 +14,8 @@
|
|||
|
||||
let settings = writable({
|
||||
fgColor: '0',
|
||||
bgColor: '0'
|
||||
bgColor: '0',
|
||||
isLoaded: false
|
||||
});
|
||||
|
||||
let status = writable({
|
||||
|
@ -25,34 +26,45 @@
|
|||
price: false,
|
||||
blocks: false
|
||||
},
|
||||
leds: []
|
||||
leds: [],
|
||||
isUpdating: false
|
||||
});
|
||||
|
||||
const fetchStatusData = () => {
|
||||
fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' })
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
status.set(data);
|
||||
});
|
||||
const fetchStatusData = async () => {
|
||||
const res = await fetch(`${PUBLIC_BASE_URL}/api/status`, { credentials: 'same-origin' });
|
||||
|
||||
if (!res.ok) {
|
||||
console.error('Error fetching status data:', res.statusText);
|
||||
return false;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
status.set(data);
|
||||
return true;
|
||||
};
|
||||
|
||||
const fetchSettingsData = () => {
|
||||
fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' })
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
data.fgColor = String(data.fgColor);
|
||||
data.bgColor = String(data.bgColor);
|
||||
data.timePerScreen = data.timerSeconds / 60;
|
||||
const fetchSettingsData = async () => {
|
||||
const res = await fetch(PUBLIC_BASE_URL + `/api/settings`, { credentials: 'same-origin' });
|
||||
|
||||
if (data.fgColor > 65535) {
|
||||
data.fgColor = '65535';
|
||||
}
|
||||
if (!res.ok) {
|
||||
console.error('Error fetching settings data:', res.statusText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.bgColor > 65535) {
|
||||
data.bgColor = '65535';
|
||||
}
|
||||
settings.set(data);
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
data.fgColor = String(data.fgColor);
|
||||
data.bgColor = String(data.bgColor);
|
||||
data.timePerScreen = data.timerSeconds / 60;
|
||||
|
||||
if (data.fgColor > 65535) {
|
||||
data.fgColor = '65535';
|
||||
}
|
||||
|
||||
if (data.bgColor > 65535) {
|
||||
data.bgColor = '65535';
|
||||
}
|
||||
settings.set(data);
|
||||
};
|
||||
|
||||
let sections: (HTMLElement | null)[];
|
||||
|
@ -89,18 +101,44 @@
|
|||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
onMount(async () => {
|
||||
setupObserver();
|
||||
|
||||
fetchSettingsData();
|
||||
fetchStatusData();
|
||||
const connectEventSource = () => {
|
||||
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
|
||||
|
||||
const evtSource = new EventSource(`${PUBLIC_BASE_URL}/events`);
|
||||
evtSource.addEventListener('status', (e) => {
|
||||
let dataObj = JSON.parse(e.data);
|
||||
status.update((s) => ({ ...s, isUpdating: true }));
|
||||
status.set(dataObj);
|
||||
});
|
||||
|
||||
evtSource.addEventListener('status', (e) => {
|
||||
let dataObj = JSON.parse(e.data);
|
||||
status.set(dataObj);
|
||||
});
|
||||
evtSource.addEventListener('message', (e) => {
|
||||
if (e.data == 'closing') {
|
||||
console.log('EventSource closing');
|
||||
status.update((s) => ({ ...s, isUpdating: false }));
|
||||
evtSource.close(); // Close the current connection
|
||||
setTimeout(connectEventSource, 5000);
|
||||
}
|
||||
});
|
||||
|
||||
evtSource.addEventListener('error', (e) => {
|
||||
console.error('EventSource failed:', e);
|
||||
status.update((s) => ({ ...s, isUpdating: false }));
|
||||
evtSource.close(); // Close the current connection
|
||||
setTimeout(connectEventSource, 1000);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
await fetchSettingsData();
|
||||
if (await fetchStatusData()) {
|
||||
settings.update((s) => ({ ...s, isLoaded: true }));
|
||||
connectEventSource();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error fetching data:', error);
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
if (observer) {
|
||||
|
@ -159,7 +197,7 @@
|
|||
</svelte:head>
|
||||
|
||||
<Container fluid>
|
||||
<Row>
|
||||
<Row class="placeholder-glow">
|
||||
<Control bind:settings on:showToast={showToast} bind:status lg="3" xxl="4"></Control>
|
||||
|
||||
<Status bind:settings bind:status lg="6" xxl="4"></Status>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
} from '@sveltestrap/sveltestrap';
|
||||
import FirmwareUpdater from './FirmwareUpdater.svelte';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { Placeholder } from '$lib/components';
|
||||
|
||||
export let settings = {};
|
||||
|
||||
|
@ -214,15 +215,16 @@
|
|||
</li>
|
||||
{/if}
|
||||
<li>
|
||||
{$_('section.control.buildTime')}: {new Date(
|
||||
$settings.lastBuildTime * 1000
|
||||
).toLocaleString()}
|
||||
{$_('section.control.buildTime')}: <Placeholder
|
||||
value={new Date($settings.lastBuildTime * 1000).toLocaleString()}
|
||||
checkValue={$settings.lastBuildTime}
|
||||
/>
|
||||
</li>
|
||||
<li>IP: {$settings.ip}</li>
|
||||
<li>HW revision: {$settings.hwRev}</li>
|
||||
<li>{$_('section.control.fwCommit')}: {$settings.gitRev}</li>
|
||||
<li>WebUI commit: {$settings.fsRev}</li>
|
||||
<li>{$_('section.control.hostname')}: {$settings.hostname}</li>
|
||||
<li>IP: <Placeholder value={$settings.ip} /></li>
|
||||
<li>HW revision: <Placeholder value={$settings.hwRev} /></li>
|
||||
<li>{$_('section.control.fwCommit')}: <Placeholder value={$settings.gitRev} /></li>
|
||||
<li>WebUI commit: <Placeholder value={$settings.fsRev} /></li>
|
||||
<li>{$_('section.control.hostname')}: <Placeholder value={$settings.hostname} /></li>
|
||||
</ul>
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
|
@ -239,7 +241,7 @@
|
|||
{#if $settings.otaEnabled}
|
||||
<hr />
|
||||
<h3>{$_('section.control.firmwareUpdate')}</h3>
|
||||
<FirmwareUpdater on:showToast bind:settings />
|
||||
<FirmwareUpdater on:showToast bind:settings bind:status />
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
import { _ } from 'svelte-i18n';
|
||||
import { writable } from 'svelte/store';
|
||||
import { Progress, Alert, Button } from '@sveltestrap/sveltestrap';
|
||||
import HourglassSplitIcon from 'svelte-bootstrap-icons/lib/HourglassSplit.svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let settings = { hwRev: '' };
|
||||
|
||||
export let status = writable({ isOTAUpdating: false });
|
||||
let currentVersion: string = $settings.gitTag; // Replace with your current version
|
||||
|
||||
let latestVersion: string = '';
|
||||
|
@ -112,6 +113,25 @@
|
|||
return binaryFilename;
|
||||
};
|
||||
|
||||
const getWebUiBinaryName = () => {
|
||||
let webuiFilename = '';
|
||||
switch ($settings.hwRev) {
|
||||
case 'REV_V8_EPD_2_13':
|
||||
webuiFilename = 'littlefs_16MB.bin';
|
||||
break;
|
||||
case 'REV_B_EPD_2_13':
|
||||
webuiFilename = 'littlefs_8MB.bin';
|
||||
break;
|
||||
case 'REV_A_EPD_2_13':
|
||||
webuiFilename = 'littlefs_4MB.bin';
|
||||
break;
|
||||
default:
|
||||
webuiFilename = 'Unsupported hardware, unable to determine WebUI binary filename';
|
||||
}
|
||||
|
||||
return webuiFilename;
|
||||
};
|
||||
|
||||
const onAutoUpdate = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -188,8 +208,12 @@
|
|||
)}: {releaseDate} -
|
||||
<a href={releaseUrl} target="_blank">{$_('section.firmwareUpdater.viewRelease')}</a><br />
|
||||
{#if isNewerVersionAvailable}
|
||||
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
||||
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
||||
{#if !$status.isOTAUpdating}
|
||||
{$_('section.firmwareUpdater.swUpdateAvailable')} -
|
||||
<a href="/" on:click={onAutoUpdate}>{$_('section.firmwareUpdater.autoUpdate')}</a>.
|
||||
{:else}
|
||||
<HourglassSplitIcon /> {$_('section.firmwareUpdater.autoUpdateInProgress')}
|
||||
{/if}
|
||||
{:else}
|
||||
{$_('section.firmwareUpdater.swUpToDate')}
|
||||
{/if}
|
||||
|
@ -199,57 +223,59 @@
|
|||
{:else}
|
||||
<p>Loading...</p>
|
||||
{/if}
|
||||
<section class="row row-cols-lg-auto align-items-end">
|
||||
<div class="col flex-fill">
|
||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||
<input
|
||||
type="file"
|
||||
id="firmwareFile"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
||||
name="update"
|
||||
class="form-control"
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
||||
>Update firmware</Button
|
||||
{#if !$status.isOTAUpdating}
|
||||
<section class="row row-cols-lg-auto align-items-end">
|
||||
<div class="col flex-fill">
|
||||
<label for="firmwareFile" class="form-label">Firmware file ({getFirmwareBinaryName()})</label>
|
||||
<input
|
||||
type="file"
|
||||
id="firmwareFile"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareUploadFile = file))}
|
||||
name="update"
|
||||
class="form-control"
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadFirmwareFile} color="primary" disabled={!firmwareUploadFile}
|
||||
>Update firmware</Button
|
||||
>
|
||||
</div>
|
||||
<div class="col flex-fill">
|
||||
<label for="webuiFile" class="form-label">WebUI file ({getWebUiBinaryName()})</label>
|
||||
<input
|
||||
type="file"
|
||||
id="webuiFile"
|
||||
name="update"
|
||||
class="form-control"
|
||||
placeholder="littlefs.bin"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
||||
>Update WebUI</Button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
{#if firmwareUploadProgress > 0}
|
||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||
>
|
||||
</div>
|
||||
<div class="col flex-fill">
|
||||
<label for="webuiFile" class="form-label">WebUI file (littlefs.bin)</label>
|
||||
<input
|
||||
type="file"
|
||||
id="webuiFile"
|
||||
name="update"
|
||||
class="form-control"
|
||||
placeholder="littlefs.bin"
|
||||
on:change={(e) => handleFileChange(e, (file) => (firmwareWebUiFile = file))}
|
||||
accept=".bin"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-fill">
|
||||
<Button block on:click={uploadWebUiFile} color="secondary" disabled={!firmwareWebUiFile}
|
||||
>Update WebUI</Button
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
{#if firmwareUploadProgress > 0}
|
||||
<Progress striped value={firmwareUploadProgress} class="progress" id="firmwareUploadProgress"
|
||||
>{$_('section.firmwareUpdater.uploading')}... {firmwareUploadProgress}%</Progress
|
||||
>
|
||||
{/if}
|
||||
{#if firmwareUploadSuccess}
|
||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if firmwareUploadSuccess}
|
||||
<Alert color="success" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadSuccess', { values: { countdown: $countdown } })}
|
||||
</Alert>
|
||||
{/if}
|
||||
|
||||
{#if firmwareUploadError}
|
||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||
{#if firmwareUploadError}
|
||||
<Alert color="danger" class="firmwareUploadStatusAlert"
|
||||
>{$_('section.firmwareUpdater.fileUploadFailed')}</Alert
|
||||
>
|
||||
{/if}
|
||||
<small
|
||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||
>
|
||||
{/if}
|
||||
<small
|
||||
>⚠️ <strong>{$_('warning')}</strong>: {$_('section.firmwareUpdater.firmwareUpdateText')}</small
|
||||
>
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { isValidNostrRelay, getPubKey, isValidHexPubKey, isValidNpub } from '$lib';
|
||||
import { PUBLIC_BASE_URL } from '$lib/config';
|
||||
import { uiSettings } from '$lib/uiSettings';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import {
|
||||
|
@ -14,42 +12,27 @@
|
|||
Form,
|
||||
Row
|
||||
} from '@sveltestrap/sveltestrap';
|
||||
import EyeIcon from 'svelte-bootstrap-icons/lib/Eye.svelte';
|
||||
import EyeSlashIcon from 'svelte-bootstrap-icons/lib/EyeSlash.svelte';
|
||||
import { derived } from 'svelte/store';
|
||||
import { SettingsSwitch, SettingsInput, SettingsSelect, ToggleHeader } from '$lib/components';
|
||||
import {
|
||||
ScreenSpecificSettings,
|
||||
DisplaySettings,
|
||||
DataSourceSettings,
|
||||
ExtraFeaturesSettings,
|
||||
SystemSettings
|
||||
} from '$lib/components/settings';
|
||||
|
||||
export let settings;
|
||||
|
||||
const wifiTxPowerMap = new Map<string, number>([
|
||||
['Default', 80],
|
||||
['19.5dBm', 78], // 19.5dBm
|
||||
['19dBm', 76], // 19dBm
|
||||
['18.5dBm', 74], // 18.5dBm
|
||||
['17dBm', 68], // 17dBm
|
||||
['15dBm', 60], // 15dBm
|
||||
['13dBm', 52], // 13dBm
|
||||
['11dBm', 44], // 11dBm
|
||||
['8.5dBm', 34], // 8.5dBm
|
||||
['7dBm', 28], // 7dBm
|
||||
['5dBm', 20] // 5dBm
|
||||
]);
|
||||
|
||||
const miningPoolMap = new Map<string, string>([
|
||||
['noderunners', 'Noderunners.network'],
|
||||
['braiins', 'Braiins Pool'],
|
||||
['ocean', 'ocean.xyz'],
|
||||
['satoshi_radio', 'Satoshi Radio pool'],
|
||||
['public_pool', 'public-pool.io'],
|
||||
['gobrrr_pool', 'Go Brrr pool']
|
||||
['gobrrr_pool', 'Go Brrr pool'],
|
||||
['ckpool', 'CKPool'],
|
||||
['eu_ckpool', 'EU CKPool']
|
||||
]);
|
||||
|
||||
const getMiningPoolName = (name: string) => {
|
||||
if (miningPoolMap.has(name)) return miningPoolMap.get(name);
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const handleReset = (e: Event) => {
|
||||
|
@ -57,20 +40,10 @@
|
|||
dispatch('formReset');
|
||||
};
|
||||
|
||||
const getTzOffsetFromSystem = () => {
|
||||
const dt = new Date();
|
||||
let diffTZ = dt.getTimezoneOffset();
|
||||
$settings.tzOffset = diffTZ * -1;
|
||||
};
|
||||
|
||||
const onSave = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
|
||||
// const form = e.target as HTMLFormElement;
|
||||
// const formData = new FormData(form);
|
||||
|
||||
let formSettings = $settings;
|
||||
|
||||
delete formSettings['gitRev'];
|
||||
delete formSettings['ip'];
|
||||
delete formSettings['lastBuildTime'];
|
||||
|
@ -79,10 +52,6 @@
|
|||
'Content-Type': 'application/json'
|
||||
});
|
||||
|
||||
//if ($settings.httpAuthEnabled) {
|
||||
// headers.set('Authorization', 'Basic ' + btoa($settings.httpAuthUser + ":" + $settings.httpAuthPass));
|
||||
//}
|
||||
|
||||
await fetch(`${PUBLIC_BASE_URL}/api/json/settings`, {
|
||||
method: 'PATCH',
|
||||
headers: headers,
|
||||
|
@ -110,110 +79,6 @@
|
|||
});
|
||||
};
|
||||
|
||||
let validNostrRelay = false;
|
||||
const testNostrRelay = async () => {
|
||||
validNostrRelay = await isValidNostrRelay($settings.nostrRelay);
|
||||
};
|
||||
|
||||
let validBitaxe = false;
|
||||
const testBitaxe = async () => {
|
||||
try {
|
||||
const response = await fetch(`http://${$settings.bitaxeHostname}/api/system/info`);
|
||||
|
||||
if (!response.ok) {
|
||||
dispatch('showToast', {
|
||||
color: 'danger',
|
||||
text: `Failed to connect to BitAxe HTTP error! status: ${response.status}`
|
||||
});
|
||||
validBitaxe = false;
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const systemInfo = await response.json();
|
||||
dispatch('showToast', {
|
||||
color: 'success',
|
||||
text: `Connected to BitAxe ${systemInfo.ASICModel} (Board version ${systemInfo.boardVersion}) running firmware ${systemInfo.version}.\r\nCurrent hashrate ${Math.round(systemInfo.hashRate)} GH/s`
|
||||
});
|
||||
validBitaxe = true;
|
||||
} catch (error) {
|
||||
if (error instanceof TypeError && error.message.includes('Failed to fetch')) {
|
||||
dispatch('showToast', {
|
||||
color: 'danger',
|
||||
text: `Failed to connect to BitAxe, make sure you are connected to the same network.`
|
||||
});
|
||||
}
|
||||
console.error('Failed to fetch Bitaxe system info:', error);
|
||||
validBitaxe = false;
|
||||
}
|
||||
};
|
||||
|
||||
const checkValidNostrPubkey = (key: string) => {
|
||||
$settings[key] = $settings[key].trim();
|
||||
if (isValidNpub($settings[key])) {
|
||||
dispatch('showToast', {
|
||||
color: 'info',
|
||||
text: $_('section.settings.convertingValidNpub')
|
||||
});
|
||||
}
|
||||
|
||||
let ret = getPubKey($settings[key]);
|
||||
|
||||
if (ret) $settings[key] = ret;
|
||||
};
|
||||
|
||||
const onFlBrightnessChange = async () => {
|
||||
await fetch(`${PUBLIC_BASE_URL}/api/frontlight/brightness/${$settings.flMaxBrightness}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let showPassword = false;
|
||||
|
||||
let textColor = '0';
|
||||
const colorStore = derived(settings, ($settings) => ({
|
||||
fgColor: $settings.fgColor,
|
||||
bgColor: $settings.bgColor
|
||||
}));
|
||||
|
||||
// $: {
|
||||
// if ($colorStore) {
|
||||
// console.log('Settings model changed:', $colorStore);
|
||||
// if ($colorStore.fgColor < $colorStore.bgColor)
|
||||
// textColor = "0";
|
||||
// else
|
||||
// textColor = "1"; // 65535
|
||||
// }
|
||||
// }
|
||||
|
||||
colorStore.subscribe(() => {
|
||||
if ($colorStore) {
|
||||
if ($colorStore.fgColor < $colorStore.bgColor) textColor = '0';
|
||||
else textColor = '1'; // 65535
|
||||
}
|
||||
});
|
||||
|
||||
const setTextColor = () => {
|
||||
console.log(textColor);
|
||||
if (textColor == '1') {
|
||||
$settings.fgColor = 65535;
|
||||
$settings.bgColor = 0;
|
||||
} else {
|
||||
$settings.fgColor = 0;
|
||||
$settings.bgColor = 65535;
|
||||
}
|
||||
};
|
||||
|
||||
const showAll = (show: boolean) => {
|
||||
screenSettingsIsOpen = show;
|
||||
displaysAndLedIsOpen = show;
|
||||
dataSourceIsOpen = show;
|
||||
extraFeaturesIsOpen = show;
|
||||
systemIsOpen = show;
|
||||
};
|
||||
|
||||
export let xs = 12;
|
||||
export let sm = xs;
|
||||
export let md = sm;
|
||||
|
@ -221,581 +86,76 @@
|
|||
export let xl = lg;
|
||||
export let xxl = xl;
|
||||
|
||||
let screenSettingsIsOpen: boolean,
|
||||
displaysAndLedIsOpen: boolean,
|
||||
dataSourceIsOpen: boolean,
|
||||
extraFeaturesIsOpen: boolean,
|
||||
systemIsOpen: boolean;
|
||||
let screenSettingsIsOpen = true,
|
||||
displaySettingsIsOpen = false,
|
||||
dataSourceIsOpen = false,
|
||||
extraFeaturesIsOpen = false,
|
||||
systemIsOpen = false;
|
||||
|
||||
const showAll = () => {
|
||||
screenSettingsIsOpen = true;
|
||||
displaySettingsIsOpen = true;
|
||||
dataSourceIsOpen = true;
|
||||
extraFeaturesIsOpen = true;
|
||||
systemIsOpen = true;
|
||||
};
|
||||
|
||||
const hideAll = () => {
|
||||
screenSettingsIsOpen = false;
|
||||
displaySettingsIsOpen = false;
|
||||
dataSourceIsOpen = false;
|
||||
extraFeaturesIsOpen = false;
|
||||
systemIsOpen = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<Col {xs} {sm} {md} {lg} {xl} {xxl} class="mb-4 mb-xl-0">
|
||||
<Card id="settings">
|
||||
<CardHeader>
|
||||
<div class="float-end">
|
||||
<small
|
||||
><button
|
||||
on:click={() => {
|
||||
showAll(true);
|
||||
}}
|
||||
type="button">{$_('section.settings.showAll')}</button
|
||||
<small>
|
||||
<button type="button" on:click={showAll} id="showAllBtn"
|
||||
>{$_('section.settings.showAll')}</button
|
||||
>
|
||||
|
|
||||
<button
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAll(false);
|
||||
}}>{$_('section.settings.hideAll')}</button
|
||||
></small
|
||||
>
|
||||
<button type="button" on:click={hideAll} id="hideAllBtn"
|
||||
>{$_('section.settings.hideAll')}</button
|
||||
>
|
||||
</small>
|
||||
</div>
|
||||
<CardTitle>{$_('section.settings.title', { default: 'Settings' })}</CardTitle>
|
||||
<CardTitle>{$_('section.settings.title')}</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
<Form on:submit={onSave} class="clearfix">
|
||||
<Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.screenSettings')}
|
||||
defaultOpen={true}
|
||||
isOpen={screenSettingsIsOpen}
|
||||
>
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="stealFocus"
|
||||
bind:checked={$settings.stealFocus}
|
||||
label={$_('section.settings.StealFocusOnNewBlock')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mcapBigChar"
|
||||
bind:checked={$settings.mcapBigChar}
|
||||
label={$_('section.settings.useBigCharsMcap')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="useBlkCountdown"
|
||||
bind:checked={$settings.useBlkCountdown}
|
||||
label={$_('section.settings.useBlkCountdown')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="useSatsSymbol"
|
||||
bind:checked={$settings.useSatsSymbol}
|
||||
label={$_('section.settings.useSatsSymbol')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="suffixPrice"
|
||||
bind:checked={$settings.suffixPrice}
|
||||
label={$_('section.settings.suffixPrice')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mowMode"
|
||||
bind:checked={$settings.mowMode}
|
||||
label={$_('section.settings.mowMode')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
disabled={!$settings.suffixPrice}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="suffixShareDot"
|
||||
bind:checked={$settings.suffixShareDot}
|
||||
label={$_('section.settings.suffixShareDot')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
disabled={!$settings.suffixPrice}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="verticalDesc"
|
||||
bind:checked={$settings.verticalDesc}
|
||||
label={$_('section.settings.verticalDesc')}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
{#if $settings.isLoaded === false}
|
||||
<div class="d-flex align-items-center">
|
||||
<strong role="status">Loading...</strong>
|
||||
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||
</div>
|
||||
{:else}
|
||||
<Form on:submit={onSave}>
|
||||
<ScreenSpecificSettings {settings} bind:isOpen={screenSettingsIsOpen} />
|
||||
<DisplaySettings {settings} bind:isOpen={displaySettingsIsOpen} />
|
||||
<DataSourceSettings {settings} bind:isOpen={dataSourceIsOpen} on:showToast />
|
||||
<ExtraFeaturesSettings
|
||||
{settings}
|
||||
bind:isOpen={extraFeaturesIsOpen}
|
||||
{miningPoolMap}
|
||||
on:showToast
|
||||
/>
|
||||
<SystemSettings {settings} bind:isOpen={systemIsOpen} />
|
||||
|
||||
{#if !$settings.actCurrencies}
|
||||
<SettingsSwitch
|
||||
id="fetchEurPrice"
|
||||
bind:checked={$settings.fetchEurPrice}
|
||||
label="{$_('section.settings.fetchEuroPrice')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
<Row>
|
||||
<h5>{$_('section.settings.screens')}</h5>
|
||||
{#if $settings.screens}
|
||||
{#each $settings.screens as s}
|
||||
<SettingsSwitch
|
||||
id="screens_{s.id}"
|
||||
bind:checked={s.enabled}
|
||||
label={s.name}
|
||||
size={$uiSettings.inputSize}
|
||||
col={{ md: '6', xl: '12', xxl: '6' }}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
{#if $settings.actCurrencies && $settings.useNostr !== true}
|
||||
<Row>
|
||||
<h5>{$_('section.settings.currencies')}</h5>
|
||||
<small>{$_('restartRequired')}</small>
|
||||
{#if $settings.availableCurrencies}
|
||||
{#each $settings.availableCurrencies as c}
|
||||
<Col md="6" xl="12" xxl="6">
|
||||
<div class="form-check form-control-{$uiSettings.inputSize}">
|
||||
<input
|
||||
id="currency_{c}"
|
||||
bind:group={$settings.actCurrencies}
|
||||
value={c}
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
bsSize={$uiSettings.inputSize}
|
||||
label={c}
|
||||
/>
|
||||
<label class="form-check-label" for="currency_{c}">{c}</label>
|
||||
</div>
|
||||
</Col>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
{/if}
|
||||
</ToggleHeader>
|
||||
</Row><Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.displaysAndLed')}
|
||||
isOpen={displaysAndLedIsOpen}
|
||||
>
|
||||
<SettingsSelect
|
||||
id="textColor"
|
||||
label={$_('section.settings.textColor')}
|
||||
bind:value={textColor}
|
||||
options={[
|
||||
[$_('colors.black') + ' on ' + $_('colors.white'), '0'],
|
||||
[$_('colors.white') + ' on ' + $_('colors.black'), '1']
|
||||
]}
|
||||
size={$uiSettings.inputSize}
|
||||
selectClass={$uiSettings.selectClass}
|
||||
onChange={setTextColor}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="timePerScreen"
|
||||
label={$_('section.settings.timePerScreen')}
|
||||
bind:value={$settings.timePerScreen}
|
||||
type="number"
|
||||
min={1}
|
||||
step="1"
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="fullRefreshMin"
|
||||
label={$_('section.settings.fullRefreshEvery')}
|
||||
bind:value={$settings.fullRefreshMin}
|
||||
type="number"
|
||||
min={1}
|
||||
step="1"
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="minSecPriceUpd"
|
||||
label={$_('section.settings.timeBetweenPriceUpdates')}
|
||||
bind:value={$settings.minSecPriceUpd}
|
||||
type="number"
|
||||
min={1}
|
||||
step="1"
|
||||
suffix={$_('time.seconds')}
|
||||
helpText={$_('section.settings.shortAmountsWarning')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="ledBrightness"
|
||||
label={$_('section.settings.ledBrightness')}
|
||||
bind:value={$settings.ledBrightness}
|
||||
type="range"
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsInput
|
||||
id="flMaxBrightness"
|
||||
label={$_('section.settings.flMaxBrightness')}
|
||||
bind:value={$settings.flMaxBrightness}
|
||||
type="range"
|
||||
min={0}
|
||||
max={4095}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={onFlBrightnessChange}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="flEffectDelay"
|
||||
label={$_('section.settings.flEffectDelay')}
|
||||
bind:value={$settings.flEffectDelay}
|
||||
type="range"
|
||||
min={5}
|
||||
max={300}
|
||||
step={1}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !$settings.flDisable && $settings.hasLightLevel}
|
||||
<SettingsInput
|
||||
id="luxLightToggle"
|
||||
label={`${$_('section.settings.luxLightToggle')} (${$settings.luxLightToggle})`}
|
||||
bind:value={$settings.luxLightToggle}
|
||||
type="range"
|
||||
min={0}
|
||||
max={1000}
|
||||
step={1}
|
||||
helpText={$_('section.settings.luxLightToggleText')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="ledTestOnPower"
|
||||
bind:checked={$settings.ledTestOnPower}
|
||||
label={$_('section.settings.ledPowerOnTest')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="ledFlashOnUpd"
|
||||
bind:checked={$settings.ledFlashOnUpd}
|
||||
label={$_('section.settings.ledFlashOnBlock')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="disableLeds"
|
||||
bind:checked={$settings.disableLeds}
|
||||
label={$_('section.settings.disableLeds')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
{#if $settings.hasFrontlight}
|
||||
<SettingsSwitch
|
||||
id="flDisable"
|
||||
bind:checked={$settings.flDisable}
|
||||
label={$_('section.settings.flDisable')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsSwitch
|
||||
id="flAlwaysOn"
|
||||
bind:checked={$settings.flAlwaysOn}
|
||||
label={$_('section.settings.flAlwaysOn')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="flFlashOnUpd"
|
||||
bind:checked={$settings.flFlashOnUpd}
|
||||
label={$_('section.settings.flFlashOnUpd')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
id="flOffWhenDark"
|
||||
bind:checked={$settings.flOffWhenDark}
|
||||
label={$_('section.settings.flOffWhenDark')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row><Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.dataSource')}
|
||||
isOpen={dataSourceIsOpen}
|
||||
>
|
||||
<SettingsInput
|
||||
id="mempoolInstance"
|
||||
label={$_('section.settings.mempoolnstance')}
|
||||
bind:value={$settings.mempoolInstance}
|
||||
disabled={$settings.ownDataSource}
|
||||
required={true}
|
||||
helpText={$_('section.settings.mempoolInstanceHelpText')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="ownDataSource"
|
||||
bind:checked={$settings.ownDataSource}
|
||||
label="{$_('section.settings.ownDataSource')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
{#if $settings.nostrRelay}
|
||||
<SettingsSwitch
|
||||
id="useNostr"
|
||||
bind:checked={$settings.useNostr}
|
||||
label="{$_('section.settings.useNostr')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if 'stagingSource' in $settings}
|
||||
<SettingsSwitch
|
||||
id="stagingSource"
|
||||
bind:checked={$settings.stagingSource}
|
||||
label="{$_('section.settings.stagingSource')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row><Row>
|
||||
<ToggleHeader
|
||||
header={$_('section.settings.section.extraFeatures')}
|
||||
isOpen={extraFeaturesIsOpen}
|
||||
>
|
||||
{#if $settings.bitaxeEnabled}
|
||||
<SettingsInput
|
||||
id="bitaxeHostname"
|
||||
label={$_('section.settings.bitaxeHostname')}
|
||||
bind:value={$settings.bitaxeHostname}
|
||||
required={true}
|
||||
valid={validBitaxe}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button type="button" color="success" on:click={testBitaxe}
|
||||
>{$_('test', { default: 'Test' })}</Button
|
||||
>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
{#if $settings.miningPoolStats}
|
||||
<SettingsSelect
|
||||
id="miningPoolName"
|
||||
label={$_('section.settings.miningPoolName')}
|
||||
bind:value={$settings.miningPoolName}
|
||||
options={$settings.availablePools.map((pool) => [getMiningPoolName(pool), pool])}
|
||||
size={$uiSettings.inputSize}
|
||||
selectClass={$uiSettings.selectClass}
|
||||
/>
|
||||
<SettingsInput
|
||||
id="miningPoolUser"
|
||||
label={$_('section.settings.miningPoolUser')}
|
||||
bind:value={$settings.miningPoolUser}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
{#if 'nostrZapNotify' in $settings && $settings['nostrZapNotify']}
|
||||
<SettingsInput
|
||||
id="nostrZapPubkey"
|
||||
label={$_('section.settings.nostrZapPubkey')}
|
||||
bind:value={$settings.nostrZapPubkey}
|
||||
required={true}
|
||||
minlength="64"
|
||||
invalid={!isValidHexPubKey($settings.nostrZapPubkey)}
|
||||
helpText={!isValidHexPubKey($settings.nostrZapPubkey)
|
||||
? $_('section.settings.invalidNostrPubkey')
|
||||
: undefined}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={() => checkValidNostrPubkey('nostrZapPubkey')}
|
||||
onInput={() => checkValidNostrPubkey('nostrZapPubkey')}
|
||||
/>
|
||||
{/if}
|
||||
{#if $settings.useNostr}
|
||||
<SettingsInput
|
||||
id="nostrPubKey"
|
||||
label={$_('section.settings.nostrPubKey')}
|
||||
bind:value={$settings.nostrPubKey}
|
||||
invalid={!isValidHexPubKey($settings.nostrPubKey)}
|
||||
helpText={!isValidHexPubKey($settings.nostrPubKey)
|
||||
? $_('section.settings.invalidNostrPubkey')
|
||||
: undefined}
|
||||
size={$uiSettings.inputSize}
|
||||
onChange={() => checkValidNostrPubkey('nostrPubKey')}
|
||||
onInput={() => checkValidNostrPubkey('nostrPubKey')}
|
||||
/>
|
||||
{/if}
|
||||
{#if 'nostrZapNotify' in $settings || $settings.useNostr}
|
||||
<SettingsInput
|
||||
id="nostrRelay"
|
||||
label={$_('section.settings.nostrRelay')}
|
||||
bind:value={$settings.nostrRelay}
|
||||
required={true}
|
||||
valid={validNostrRelay}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button type="button" color="success" on:click={testNostrRelay}
|
||||
>{$_('test', { default: 'Test' })}</Button
|
||||
>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
<Row>
|
||||
{#if 'bitaxeEnabled' in $settings}
|
||||
<SettingsSwitch
|
||||
id="bitaxeEnabled"
|
||||
bind:checked={$settings.bitaxeEnabled}
|
||||
label="{$_('section.settings.bitaxeEnabled')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
{#if 'miningPoolStats' in $settings}
|
||||
<SettingsSwitch
|
||||
id="miningPoolStats"
|
||||
bind:checked={$settings.miningPoolStats}
|
||||
label="{$_('section.settings.miningPoolStats')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
{#if 'nostrZapNotify' in $settings}
|
||||
<SettingsSwitch
|
||||
id="nostrZapNotify"
|
||||
bind:checked={$settings.nostrZapNotify}
|
||||
label="{$_('section.settings.nostrZapNotify')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="ledFlashOnZap"
|
||||
bind:checked={$settings.ledFlashOnZap}
|
||||
label={$_('section.settings.ledFlashOnZap')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{#if $settings.hasFrontlight && !$settings.flDisable}
|
||||
<SettingsSwitch
|
||||
id="flFlashOnZap"
|
||||
bind:checked={$settings.flFlashOnZap}
|
||||
label={$_('section.settings.flFlashOnZap')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row><Row>
|
||||
<ToggleHeader header={$_('section.settings.section.system')} isOpen={systemIsOpen}>
|
||||
<SettingsInput
|
||||
id="tzOffset"
|
||||
label={$_('section.settings.timezoneOffset')}
|
||||
bind:value={$settings.tzOffset}
|
||||
type="number"
|
||||
step="1"
|
||||
required={true}
|
||||
suffix={$_('time.minutes')}
|
||||
helpText={$_('section.settings.tzOffsetHelpText')}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button type="button" color="info" on:click={getTzOffsetFromSystem}
|
||||
>{$_('auto-detect')}</Button
|
||||
>
|
||||
</SettingsInput>
|
||||
|
||||
{#if $settings.httpAuthEnabled}
|
||||
<SettingsInput
|
||||
id="httpAuthUser"
|
||||
label={$_('section.settings.httpAuthUser')}
|
||||
bind:value={$settings.httpAuthUser}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsInput
|
||||
id="httpAuthPass"
|
||||
label={$_('section.settings.httpAuthPass')}
|
||||
bind:value={$settings.httpAuthPass}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
required={true}
|
||||
size={$uiSettings.inputSize}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
on:click={() => (showPassword = !showPassword)}
|
||||
color={showPassword ? 'success' : 'danger'}
|
||||
>
|
||||
{#if !showPassword}<EyeIcon />{:else}<EyeSlashIcon />{/if}
|
||||
</Button>
|
||||
</SettingsInput>
|
||||
{/if}
|
||||
|
||||
<SettingsInput
|
||||
id="hostnamePrefix"
|
||||
label={$_('section.settings.hostnamePrefix')}
|
||||
bind:value={$settings.hostnamePrefix}
|
||||
required={true}
|
||||
minlength="1"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<SettingsSelect
|
||||
id="wifiTxPower"
|
||||
label={$_('section.settings.wifiTxPower', { default: 'WiFi Tx Power' })}
|
||||
bind:value={$settings.txPower}
|
||||
options={Array.from(wifiTxPowerMap.entries())}
|
||||
size={$uiSettings.inputSize}
|
||||
selectClass={$uiSettings.selectClass}
|
||||
helpText={$_('section.settings.wifiTxPowerText')}
|
||||
/>
|
||||
|
||||
<SettingsInput
|
||||
id="wpTimeout"
|
||||
label={$_('section.settings.wpTimeout')}
|
||||
bind:value={$settings.wpTimeout}
|
||||
type="number"
|
||||
min={1}
|
||||
step="1"
|
||||
required={true}
|
||||
suffix={$_('time.seconds')}
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<SettingsSwitch
|
||||
id="otaEnabled"
|
||||
bind:checked={$settings.otaEnabled}
|
||||
label="{$_('section.settings.otaUpdates')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="mdnsEnabled"
|
||||
bind:checked={$settings.mdnsEnabled}
|
||||
label="{$_('section.settings.enableMdns')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
<SettingsSwitch
|
||||
id="httpAuthEnabled"
|
||||
bind:checked={$settings.httpAuthEnabled}
|
||||
label="{$_('section.settings.httpAuthEnabled')} ({$_('restartRequired')})"
|
||||
size={$uiSettings.inputSize}
|
||||
/>
|
||||
</Row>
|
||||
</ToggleHeader>
|
||||
</Row>
|
||||
|
||||
<Row>
|
||||
<Col class="d-flex justify-content-end">
|
||||
<Button on:click={handleReset} color="secondary">{$_('button.reset')}</Button>
|
||||
<div class="mx-2"></div>
|
||||
<Button color="primary">{$_('button.save')}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
<Row class="mt-4">
|
||||
<Col>
|
||||
<Button type="submit" color="primary" class="me-2">
|
||||
{$_('button.save')}
|
||||
</Button>
|
||||
<Button type="button" color="secondary" on:click={handleReset}>
|
||||
{$_('button.reset')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
Row
|
||||
} from '@sveltestrap/sveltestrap';
|
||||
import Rendered from './Rendered.svelte';
|
||||
import { DataSourceType } from '$lib/types/dataSource';
|
||||
|
||||
export let settings;
|
||||
export let status: writable<object>;
|
||||
|
@ -74,7 +75,7 @@
|
|||
});
|
||||
|
||||
settings.subscribe((value: object) => {
|
||||
lightMode = value.bgColor > value.fgColor;
|
||||
lightMode = !value.invertedColor;
|
||||
|
||||
if (value.screens) buttonChunks = chunkArray(value.screens, 5);
|
||||
});
|
||||
|
@ -96,6 +97,16 @@
|
|||
}
|
||||
};
|
||||
|
||||
const toggleDoNotDisturb = (currentStatus: boolean) => (e: Event) => {
|
||||
e.preventDefault();
|
||||
console.log(currentStatus);
|
||||
if (!currentStatus) {
|
||||
fetch(`${PUBLIC_BASE_URL}/api/dnd/enable`);
|
||||
} else {
|
||||
fetch(`${PUBLIC_BASE_URL}/api/dnd/disable`);
|
||||
}
|
||||
};
|
||||
|
||||
export let xs = 12;
|
||||
export let sm = xs;
|
||||
export let md = sm;
|
||||
|
@ -110,11 +121,29 @@
|
|||
<CardTitle>{$_('section.status.title', { default: 'Status' })}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
{#if $settings.screens}
|
||||
<div class=" d-block d-sm-none mx-auto text-center">
|
||||
{#each buttonChunks as chunk}
|
||||
<ButtonGroup size="sm" class="mx-auto mb-1">
|
||||
{#each chunk as s}
|
||||
{#if $settings.isLoaded === false}
|
||||
<div class="d-flex align-items-center">
|
||||
<strong role="status">Loading...</strong>
|
||||
<div class="spinner-border ms-auto" aria-hidden="true"></div>
|
||||
</div>
|
||||
{:else}
|
||||
{#if $settings.screens}
|
||||
<div class=" d-block d-sm-none mx-auto text-center">
|
||||
{#each buttonChunks as chunk}
|
||||
<ButtonGroup size="sm" class="mx-auto mb-1">
|
||||
{#each chunk as s}
|
||||
<Button
|
||||
color="outline-primary"
|
||||
active={$status.currentScreen == s.id}
|
||||
on:click={setScreen(s.id)}>{s.name}</Button
|
||||
>
|
||||
{/each}
|
||||
</ButtonGroup>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="d-flex justify-content-center d-none d-sm-flex">
|
||||
<ButtonGroup size="sm">
|
||||
{#each $settings.screens as s}
|
||||
<Button
|
||||
color="outline-primary"
|
||||
active={$status.currentScreen == s.id}
|
||||
|
@ -122,148 +151,169 @@
|
|||
>
|
||||
{/each}
|
||||
</ButtonGroup>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="d-flex justify-content-center d-none d-sm-flex">
|
||||
<ButtonGroup size="sm">
|
||||
{#each $settings.screens as s}
|
||||
<Button
|
||||
color="outline-primary"
|
||||
active={$status.currentScreen == s.id}
|
||||
on:click={setScreen(s.id)}>{s.name}</Button
|
||||
>
|
||||
{/each}
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
{#if $settings.actCurrencies && $settings.ownDataSource}
|
||||
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
||||
<ButtonGroup size="sm">
|
||||
{#each $settings.actCurrencies as c}
|
||||
<Button
|
||||
color="outline-success"
|
||||
active={$status.currency == c}
|
||||
on:click={setCurrency(c)}>{c}</Button
|
||||
>
|
||||
{/each}
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
<hr />
|
||||
{#if $status.data}
|
||||
<section class={lightMode ? 'lightMode' : 'darkMode'}>
|
||||
<Rendered
|
||||
status={$status}
|
||||
className="btclock-wrapper"
|
||||
verticalDesc={$settings.verticalDesc}
|
||||
></Rendered>
|
||||
</section>
|
||||
{$_('section.status.screenCycle')}:
|
||||
<a
|
||||
id="timerStatusText"
|
||||
href={'#'}
|
||||
style="cursor: pointer"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-pressed="false"
|
||||
on:click={toggleTimer($status.timerRunning)}
|
||||
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
||||
'timer.stopped'
|
||||
)}{/if}</a
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
<hr />
|
||||
{#if !$settings.disableLeds}
|
||||
<Row class="justify-content-evenly">
|
||||
{#if $status.leds}
|
||||
{#each $status.leds as led}
|
||||
<Col>
|
||||
<Input
|
||||
type="color"
|
||||
id="ledColorPicker"
|
||||
bind:value={led.hex}
|
||||
class="mx-auto"
|
||||
disabled
|
||||
/>
|
||||
</Col>
|
||||
{/each}
|
||||
{#if $settings.actCurrencies && ($settings.dataSource == DataSourceType.BTCLOCK_SOURCE || $settings.dataSource == DataSourceType.CUSTOM_SOURCE)}
|
||||
<div class="d-flex justify-content-center d-sm-flex mt-2">
|
||||
<ButtonGroup size="sm">
|
||||
{#each $settings.actCurrencies as c}
|
||||
<Button
|
||||
color="outline-success"
|
||||
active={$status.currency == c}
|
||||
on:click={setCurrency(c)}>{c}</Button
|
||||
>
|
||||
{/each}
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
</Row>
|
||||
<hr />
|
||||
{/if}
|
||||
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>{$_('section.status.memoryFree')}</div>
|
||||
<div>
|
||||
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{#if $settings.hasLightLevel}
|
||||
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
||||
<hr />
|
||||
{/if}
|
||||
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
||||
>{rssiPercent}%</Progress
|
||||
>
|
||||
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
||||
<hr />
|
||||
{#if $status.data}
|
||||
<section class={lightMode ? 'lightMode' : 'darkMode'} style="position: relative;">
|
||||
{#if $status.isUpdating === false && ($status.isFake ?? false) === false}
|
||||
<div class="connection-lost-overlay">
|
||||
<div class="overlay-content">
|
||||
<i class="bi bi-wifi-off"></i>
|
||||
<h4>Lost connection</h4>
|
||||
<p>Trying to reconnect...</p>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<Rendered
|
||||
status={$status}
|
||||
className="btclock-wrapper"
|
||||
verticalDesc={$settings.verticalDesc}
|
||||
></Rendered>
|
||||
</section>
|
||||
{$_('section.status.screenCycle')}:
|
||||
<a
|
||||
id="timerStatusText"
|
||||
href={'#'}
|
||||
style="cursor: pointer"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-pressed="false"
|
||||
on:click={toggleTimer($status.timerRunning)}
|
||||
>{#if $status.timerRunning}⏵ {$_('timer.running')}{:else}⏸ {$_(
|
||||
'timer.stopped'
|
||||
)}{/if}</a
|
||||
><br />
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>{$_('section.status.wifiSignalStrength')}</div>
|
||||
<div>
|
||||
{$status.rssi} dBm
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
||||
<br />
|
||||
<p>
|
||||
{#if $settings.useNostr || $settings.nostrZapNotify}
|
||||
{$_('section.status.nostrConnection')}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span>
|
||||
{$_('section.status.doNotDisturb')}:
|
||||
<a
|
||||
id="dndStatusText"
|
||||
href={'#'}
|
||||
style="cursor: pointer"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-pressed="false"
|
||||
on:click={toggleDoNotDisturb($status.dnd?.enabled)}
|
||||
>
|
||||
{#if $status.dnd?.active}⏵ {$_('on')}{:else}⏸ {$_('off')}{/if}</a
|
||||
>
|
||||
<small>
|
||||
{#if $status.dnd?.timeBasedEnabled}
|
||||
{$_('section.status.timeBasedDnd')} ( {$settings.dnd
|
||||
.startHour}:{$settings.dnd.startMinute.toString().padStart(2, '0')} - {$settings
|
||||
.dnd.endHour}:{$settings.dnd.endMinute.toString().padStart(2, '0')} )
|
||||
{/if}
|
||||
</small>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !$settings.useNostr}
|
||||
{#if !$settings.ownDataSource}
|
||||
{$_('section.status.wsPriceConnection')}:
|
||||
<hr />
|
||||
{#if !$settings.disableLeds}
|
||||
<Row class="justify-content-evenly">
|
||||
{#if $status.leds}
|
||||
{#each $status.leds as led}
|
||||
<Col>
|
||||
<Input
|
||||
type="color"
|
||||
id="ledColorPicker"
|
||||
bind:value={led.hex}
|
||||
class="mx-auto"
|
||||
disabled
|
||||
/>
|
||||
</Col>
|
||||
{/each}
|
||||
{/if}
|
||||
</Row>
|
||||
<hr />
|
||||
{/if}
|
||||
<Progress striped value={memoryFreePercent}>{memoryFreePercent}%</Progress>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>{$_('section.status.memoryFree')}</div>
|
||||
<div>
|
||||
{Math.round($status.espFreeHeap / 1024)} / {Math.round($status.espHeapSize / 1024)} KiB
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{#if $settings.hasLightLevel}
|
||||
{$_('section.status.lightSensor')}: {Number(Math.round($status.lightLevel))} lux
|
||||
<hr />
|
||||
{/if}
|
||||
<Progress striped id="rssiBar" color={wifiStrengthColor} value={rssiPercent}
|
||||
>{rssiPercent}%</Progress
|
||||
>
|
||||
<Tooltip target="rssiBar" placement="bottom">{$_('rssiBar.tooltip')}</Tooltip>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>{$_('section.status.wifiSignalStrength')}</div>
|
||||
<div>
|
||||
{$status.rssi} dBm
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
{$_('section.status.uptime')}: {toUptimestring($status.espUptime)}
|
||||
<br />
|
||||
<p>
|
||||
{#if $settings.dataSource == DataSourceType.NOSTR_SOURCE || $settings.nostrZapNotify}
|
||||
{$_('section.status.nostrConnection')}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.price}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span>
|
||||
-
|
||||
{$_('section.status.wsMempoolConnection', {
|
||||
values: { instance: $settings.mempoolInstance }
|
||||
})}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span><br />
|
||||
{:else}
|
||||
{$_('section.status.wsDataConnection')}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
||||
{#if $status.connectionStatus && $status.connectionStatus.nostr}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if $settings.fetchEurPrice}
|
||||
<small>{$_('section.status.fetchEuroNote')}</small>
|
||||
{/if}
|
||||
</p>
|
||||
{#if $settings.dataSource != DataSourceType.NOSTR_SOURCE}
|
||||
{#if $settings.dataSource == DataSourceType.THIRD_PARTY_SOURCE}
|
||||
{$_('section.status.wsPriceConnection')}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.price}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span>
|
||||
-
|
||||
{$_('section.status.wsMempoolConnection', {
|
||||
values: { instance: $settings.mempoolInstance }
|
||||
})}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.blocks}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span><br />
|
||||
{:else}
|
||||
{$_('section.status.wsDataConnection')}:
|
||||
<span>
|
||||
{#if $status.connectionStatus && $status.connectionStatus.V2}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if $settings.fetchEurPrice}
|
||||
<small>{$_('section.status.fetchEuroNote')}</small>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
|
70
tests/doc-screenshots/viewport-screenshots.spec.ts
Normal file
70
tests/doc-screenshots/viewport-screenshots.spec.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
import { initMock, settingsJson, statusJson } from '../shared';
|
||||
import sharp from 'sharp';
|
||||
|
||||
test.beforeEach(initMock);
|
||||
|
||||
// Define the translations for the headings
|
||||
const headings = {
|
||||
en: {
|
||||
control: 'Control',
|
||||
status: 'Status',
|
||||
settings: 'Settings',
|
||||
language: 'English'
|
||||
},
|
||||
de: {
|
||||
control: 'Steuerung',
|
||||
status: 'Status',
|
||||
settings: 'Einstellungen',
|
||||
language: 'Deutsch'
|
||||
},
|
||||
nl: {
|
||||
control: 'Besturing',
|
||||
status: 'Status',
|
||||
settings: 'Instellingen',
|
||||
language: 'Nederlands'
|
||||
},
|
||||
es: {
|
||||
control: 'Control',
|
||||
status: 'Estado',
|
||||
settings: 'Ajustes',
|
||||
language: 'Español'
|
||||
}
|
||||
};
|
||||
|
||||
test('capture screenshots across devices', async ({ page }, testInfo) => {
|
||||
// Get the locale from the browser or default to 'en'
|
||||
const locale = testInfo.project.use?.locale?.split('-')[0].toLowerCase() || 'en';
|
||||
const translations = headings[locale] || headings.en;
|
||||
|
||||
statusJson.isUpdating = true;
|
||||
// Set the color scheme
|
||||
if (testInfo.project.use?.colorScheme === 'dark') {
|
||||
settingsJson.invertedColor = true;
|
||||
} else {
|
||||
settingsJson.invertedColor = false;
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await expect(page.getByRole('heading', { name: translations.control })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: translations.status })).toBeVisible();
|
||||
await expect(page.getByRole('heading', { name: translations.settings })).toBeVisible();
|
||||
|
||||
if (await page.locator('#nav-language-dropdown').isVisible()) {
|
||||
await expect(page.getByRole('link', { name: translations.language })).toBeVisible();
|
||||
}
|
||||
|
||||
const screenshot = await page.screenshot({
|
||||
fullPage: true
|
||||
});
|
||||
|
||||
await sharp(screenshot)
|
||||
.toFormat('webp', {
|
||||
quality: 95,
|
||||
nearLossless: true
|
||||
})
|
||||
.toFile(
|
||||
`./doc/screenshot-${test.info().project.use.colorScheme?.toLowerCase().replace(' ', '_')}.webp`
|
||||
);
|
||||
});
|
|
@ -132,7 +132,7 @@ test('screens should be able to change', async ({ page }) => {
|
|||
|
||||
await page.getByRole('button', { name: 'Sats per Dollar' }).click();
|
||||
const response = await responsePromise;
|
||||
expect(response.url()).toContain('api/show/screen/1');
|
||||
expect(response.url()).toContain('api/show/screen/10');
|
||||
});
|
||||
|
||||
test('parse all types of EPD content correctly', async ({ page }) => {
|
||||
|
|
170
tests/shared.ts
170
tests/shared.ts
|
@ -1,33 +1,81 @@
|
|||
interface Page {
|
||||
route: (url: string, handler: (route: Route) => Promise<void>) => Promise<void>;
|
||||
}
|
||||
|
||||
interface Route {
|
||||
fulfill: (response: {
|
||||
json?: typeof statusJson | typeof settingsJson | typeof latestReleaseFake;
|
||||
status?: number;
|
||||
headers?: Record<string, string>;
|
||||
body?: ReadableStream;
|
||||
}) => Promise<void>;
|
||||
}
|
||||
|
||||
export const fetchLatestBlockHeight = async () => {
|
||||
const response = await fetch('https://ws.btclock.dev/api/lastblock');
|
||||
const blockHeight = await response.text();
|
||||
return ['BLOCK/HEIGHT', ...blockHeight.trim().split('')];
|
||||
};
|
||||
|
||||
export const fetchLatestRelease = async () => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/latest'
|
||||
);
|
||||
if (!response.ok) throw new Error('Failed to fetch latest release');
|
||||
const data = await response.json();
|
||||
settingsJson.gitTag = data.tag_name;
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch latest release, using fallback:', error);
|
||||
settingsJson.gitTag = latestReleaseFake.tag_name;
|
||||
return latestReleaseFake;
|
||||
}
|
||||
};
|
||||
|
||||
export const statusJson = {
|
||||
currentScreen: 0,
|
||||
currentScreen: 20,
|
||||
numScreens: 7,
|
||||
timerRunning: true,
|
||||
isOTAUpdating: false,
|
||||
espUptime: 4479,
|
||||
espFreeHeap: 58508,
|
||||
espHeapSize: 342108,
|
||||
connectionStatus: { price: true, blocks: true },
|
||||
connectionStatus: {
|
||||
price: false,
|
||||
blocks: false,
|
||||
V2: true,
|
||||
nostr: true
|
||||
},
|
||||
rssi: -66,
|
||||
data: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
||||
rendered: ['BLOCK/HEIGHT', '8', '1', '8', '0', '2', '6'],
|
||||
data: ['BLOCK/HEIGHT', '0', '0', '0', '0', '0', '0'],
|
||||
currency: 'USD',
|
||||
leds: [
|
||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||
{ red: 0, green: 0, blue: 0, hex: '#000000' },
|
||||
{ red: 0, green: 0, blue: 0, hex: '#000000' }
|
||||
]
|
||||
],
|
||||
isUpdating: true,
|
||||
isFake: true,
|
||||
dnd: {
|
||||
enabled: true,
|
||||
timeBasedEnabled: true,
|
||||
startTime: '23:00',
|
||||
endTime: '7:00',
|
||||
active: true
|
||||
}
|
||||
};
|
||||
|
||||
export const settingsJson = {
|
||||
numScreens: 7,
|
||||
fgColor: 415029,
|
||||
bgColor: 0,
|
||||
timerSeconds: 1800,
|
||||
timerRunning: true,
|
||||
minSecPriceUpd: 30,
|
||||
fullRefreshMin: 60,
|
||||
wpTimeout: 600,
|
||||
tzOffset: 0,
|
||||
useBitcoinNode: false,
|
||||
dataSource: 0,
|
||||
mempoolInstance: 'mempool.space',
|
||||
ledTestOnPower: true,
|
||||
ledFlashOnUpd: true,
|
||||
|
@ -42,7 +90,7 @@ export const settingsJson = {
|
|||
ip: '192.168.20.231',
|
||||
txPower: 78,
|
||||
gitRev: '25d8b92bcbc8938417c140355ea3ba99ff9eb4b7',
|
||||
gitTag: '3.1.9',
|
||||
gitTag: '3.2.27',
|
||||
bitaxeEnabled: false,
|
||||
bitaxeHostname: 'bitaxe1',
|
||||
miningPoolStats: false,
|
||||
|
@ -50,9 +98,9 @@ export const settingsJson = {
|
|||
miningPoolUser: '38Qkkei3SuF1Eo45BaYmRHUneRD54yyTFy',
|
||||
nostrZapNotify: true,
|
||||
hwRev: 'REV_A_EPD_2_13',
|
||||
fsRev: '4c5d9616212b27e3f05c35370f0befcf2c5a04b2',
|
||||
fsRev: '64e518bf58f89749753167a8b6826e10bb6455c5',
|
||||
nostrZapPubkey: 'b5127a08cf33616274800a4387881a9f98e04b9c37116e92de5250498635c422',
|
||||
lastBuildTime: '1700666677',
|
||||
lastBuildTime: Math.round(new Date().getTime() / 1000),
|
||||
screens: [
|
||||
{
|
||||
id: 0,
|
||||
|
@ -62,17 +110,17 @@ export const settingsJson = {
|
|||
{
|
||||
id: 3,
|
||||
name: 'Time',
|
||||
enabled: true
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Halving countdown',
|
||||
enabled: true
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Block Fee Rate',
|
||||
enabled: true
|
||||
enabled: false
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
|
@ -87,32 +135,79 @@ export const settingsJson = {
|
|||
{
|
||||
id: 30,
|
||||
name: 'Market Cap',
|
||||
enabled: true
|
||||
enabled: false
|
||||
}
|
||||
],
|
||||
actCurrencies: ['USD', 'EUR'],
|
||||
availableCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD']
|
||||
availableCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD'],
|
||||
availablePools: [
|
||||
'ocean',
|
||||
'noderunners',
|
||||
'satoshi_radio',
|
||||
'braiins',
|
||||
'public_pool',
|
||||
'gobrrr_pool',
|
||||
'ckpool',
|
||||
'eu_ckpool'
|
||||
],
|
||||
dnd: {
|
||||
enabled: false,
|
||||
timeBasedEnabled: true,
|
||||
startHour: 23,
|
||||
startMinute: 0,
|
||||
endHour: 7,
|
||||
endMinute: 0
|
||||
},
|
||||
availableFonts: ['antonio', 'oswald'],
|
||||
invertedColor: false,
|
||||
isLoaded: true,
|
||||
isFake: true
|
||||
};
|
||||
|
||||
export const initMock = async ({ page }) => {
|
||||
export const latestReleaseFake = {
|
||||
id: 782,
|
||||
tag_name: '3.2.24',
|
||||
target_commitish: '',
|
||||
name: '3.2.24',
|
||||
body: '',
|
||||
url: 'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/782',
|
||||
html_url: 'https://git.btclock.dev/btclock/btclock_v3/releases/tag/3.2.24',
|
||||
tarball_url: 'https://git.btclock.dev/btclock/btclock_v3/archive/3.2.24.tar.gz',
|
||||
zipball_url: 'https://git.btclock.dev/btclock/btclock_v3/archive/3.2.24.zip',
|
||||
hide_archive_links: false,
|
||||
upload_url: 'https://git.btclock.dev/api/v1/repos/btclock/btclock_v3/releases/782/assets',
|
||||
draft: false,
|
||||
prerelease: false,
|
||||
created_at: '2024-12-28T17:48:05Z',
|
||||
published_at: '2024-12-28T17:48:05Z',
|
||||
author: {},
|
||||
assets: [],
|
||||
archive_download_count: {
|
||||
zip: 0,
|
||||
tar_gz: 0
|
||||
}
|
||||
};
|
||||
|
||||
export const initMock = async ({ page }: { page: Page }) => {
|
||||
// Update status with latest block height
|
||||
statusJson.data = await fetchLatestBlockHeight();
|
||||
const latestRelease = await fetchLatestRelease();
|
||||
|
||||
await page.route('*/**/api/status', async (route) => {
|
||||
await route.fulfill({ json: statusJson });
|
||||
});
|
||||
|
||||
await page.route('*/**/api/show/screen/1', async (route) => {
|
||||
await page.route('*/**/api/show/screen/10', async (route) => {
|
||||
//if (route.request().url().includes('*/**/api/show/screen/1')) {
|
||||
statusJson.currentScreen = 1;
|
||||
statusJson.data = ['MSCW/TIME', ' ', ' ', '2', '6', '4', '4'];
|
||||
statusJson.rendered = statusJson.data;
|
||||
//}
|
||||
|
||||
await route.fulfill({ json: statusJson });
|
||||
});
|
||||
|
||||
await page.route('*/**/api/show/screen/2', async (route) => {
|
||||
await page.route('*/**/api/show/screen/20', async (route) => {
|
||||
statusJson.currentScreen = 2;
|
||||
statusJson.data = ['BTC/USD', '$', '3', '7', '8', '2', '4'];
|
||||
statusJson.rendered = statusJson.data;
|
||||
|
||||
await route.fulfill({ json: statusJson });
|
||||
});
|
||||
|
@ -120,7 +215,6 @@ export const initMock = async ({ page }) => {
|
|||
await page.route('*/**/api/show/screen/4', async (route) => {
|
||||
statusJson.currentScreen = 4;
|
||||
statusJson.data = ['BIT/COIN', 'HALV/ING', '0/YRS', '149/DAYS', '8/HRS', '30/MINS', 'TO/GO'];
|
||||
statusJson.rendered = statusJson.data;
|
||||
|
||||
await route.fulfill({ json: statusJson });
|
||||
});
|
||||
|
@ -129,15 +223,35 @@ export const initMock = async ({ page }) => {
|
|||
await route.fulfill({ json: settingsJson });
|
||||
});
|
||||
|
||||
await page.route('**/events', (route) => {
|
||||
await page.route('**/events', async (route) => {
|
||||
const newStatus = statusJson;
|
||||
newStatus.data = ['BLOCK/HEIGHT', '8', '0', '0', '8', '1', '5'];
|
||||
newStatus.isUpdating = true;
|
||||
|
||||
// Respond with a custom SSE message
|
||||
route.fulfill({
|
||||
// Format the SSE message correctly
|
||||
const sseMessage = `data: ${JSON.stringify(newStatus)}\n\n`;
|
||||
|
||||
// Create a readable stream for SSE
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode(sseMessage));
|
||||
// Keep the connection open
|
||||
// controller.close(); // Don't close if you want to send more events
|
||||
}
|
||||
});
|
||||
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'text/event-stream',
|
||||
json: `${JSON.stringify(newStatus)}\n\n`
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
Connection: 'keep-alive'
|
||||
},
|
||||
body: stream
|
||||
});
|
||||
});
|
||||
|
||||
await page.route('**/api/v1/repos/btclock/btclock_v3/releases/latest', async (route) => {
|
||||
await route.fulfill({ json: latestRelease });
|
||||
});
|
||||
};
|
||||
|
|
18
vite.config.test.ts
Normal file
18
vite.config.test.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
build: {
|
||||
sourcemap: true,
|
||||
minify: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: undefined // Disable code splitting
|
||||
}
|
||||
}
|
||||
},
|
||||
test: {
|
||||
include: ['tests/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
});
|
|
@ -1,6 +1,5 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import GithubActionsReporter from 'vitest-github-actions-reporter';
|
||||
// import { visualizer } from 'rollup-plugin-visualizer';
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
@ -100,8 +99,7 @@ export default defineConfig({
|
|||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
reporters: process.env.GITHUB_ACTIONS ? ['default', new GithubActionsReporter()] : 'default'
|
||||
environment: 'jsdom'
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
|
||||
|
|
401
yarn.lock
401
yarn.lock
|
@ -2,22 +2,6 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@actions/core@^1.10.0":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.10.1.tgz#61108e7ac40acae95ee36da074fa5850ca4ced8a"
|
||||
integrity sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==
|
||||
dependencies:
|
||||
"@actions/http-client" "^2.0.1"
|
||||
uuid "^8.3.2"
|
||||
|
||||
"@actions/http-client@^2.0.1":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.2.0.tgz#f8239f375be6185fcd07765efdcf0031ad5df1a0"
|
||||
integrity sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==
|
||||
dependencies:
|
||||
tunnel "^0.0.6"
|
||||
undici "^5.25.4"
|
||||
|
||||
"@adobe/css-tools@^4.4.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.0.tgz#728c484f4e10df03d5a3acd0d8adcbbebff8ad63"
|
||||
|
@ -52,6 +36,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@emnapi/runtime@^1.2.0":
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60"
|
||||
integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@esbuild/aix-ppc64@0.19.12":
|
||||
version "0.19.12"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f"
|
||||
|
@ -343,19 +334,19 @@
|
|||
levn "^0.4.1"
|
||||
|
||||
"@fontsource/antonio@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/antonio/-/antonio-5.1.0.tgz#e3159a104d364629852ffc5170764f619a8c5754"
|
||||
integrity sha512-5FHPDnNXhLg/ccgTSOzqL0v8TqUIYw+HXYXVKsA97dYXm+SpkypXW/R/bTe8eNHVarnNRfYkhYyZq6dKA9/vGQ==
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/antonio/-/antonio-5.1.1.tgz#043c5493519d275c5f2d6963b7a0a9e7b0998fcc"
|
||||
integrity sha512-KoakMjWG/OjcvJuPeTaM0U8B84o2jaQvJA3jVodXJcn0L07AJxxmbgRbLt/9u+4ioMaXPSDwytG16eiDMCmWiA==
|
||||
|
||||
"@fontsource/oswald@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/oswald/-/oswald-5.1.0.tgz#00fdbe5ab911bfb261ec26e565eb51d02cb95095"
|
||||
integrity sha512-MwHMRCNCeTvaUWk15AHVfUi84T4TB4dshJrCOcfBYqUkY0V8IxKEwqFzSgGBG7zIfTZ0Diu+/CKpiktKqo5Gkg==
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/oswald/-/oswald-5.1.1.tgz#fdf40cca1d5c02c9b0cc0d5ac20eacb71a01731d"
|
||||
integrity sha512-MXymZDN08CJ4q/5+ur6u7YM3xcgPf6OXzUy7ngrRdGYXR9Oxal1A1m3FhFVLP5X0pWW6l2W1awC9YkTGWrbsXg==
|
||||
|
||||
"@fontsource/ubuntu@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/ubuntu/-/ubuntu-5.1.0.tgz#aad73a6540659e1005e181029cb3f5efd06a438f"
|
||||
integrity sha512-0XG/HrFsfP1q3phf4QN8IO7tetd0zOZKHZSHcTnBuVoQedoo1wS/hXxY2FMZuqoG+mVfrXh+Q614MDVmQPJq2w==
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@fontsource/ubuntu/-/ubuntu-5.1.1.tgz#818d9a722c4b8ebb202bddedc9d1519f15c8f3d6"
|
||||
integrity sha512-ghyIHIsqbBkUMA3zjvCIQENHl4CEhNgKrVxsuuC46XKw5Yfiy4C3W/A0eLGBR4+yre39b/1sVVsjK6VByVMAtg==
|
||||
|
||||
"@formatjs/ecma402-abstract@2.2.0":
|
||||
version "2.2.0"
|
||||
|
@ -425,6 +416,119 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b"
|
||||
integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==
|
||||
|
||||
"@img/sharp-darwin-arm64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08"
|
||||
integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-darwin-arm64" "1.0.4"
|
||||
|
||||
"@img/sharp-darwin-x64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61"
|
||||
integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-darwin-x64" "1.0.4"
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f"
|
||||
integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==
|
||||
|
||||
"@img/sharp-libvips-darwin-x64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062"
|
||||
integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==
|
||||
|
||||
"@img/sharp-libvips-linux-arm64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704"
|
||||
integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==
|
||||
|
||||
"@img/sharp-libvips-linux-arm@1.0.5":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197"
|
||||
integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==
|
||||
|
||||
"@img/sharp-libvips-linux-s390x@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce"
|
||||
integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==
|
||||
|
||||
"@img/sharp-libvips-linux-x64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0"
|
||||
integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5"
|
||||
integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64@1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff"
|
||||
integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==
|
||||
|
||||
"@img/sharp-linux-arm64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22"
|
||||
integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-arm64" "1.0.4"
|
||||
|
||||
"@img/sharp-linux-arm@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff"
|
||||
integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-arm" "1.0.5"
|
||||
|
||||
"@img/sharp-linux-s390x@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667"
|
||||
integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-s390x" "1.0.4"
|
||||
|
||||
"@img/sharp-linux-x64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb"
|
||||
integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linux-x64" "1.0.4"
|
||||
|
||||
"@img/sharp-linuxmusl-arm64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b"
|
||||
integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
|
||||
|
||||
"@img/sharp-linuxmusl-x64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48"
|
||||
integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==
|
||||
optionalDependencies:
|
||||
"@img/sharp-libvips-linuxmusl-x64" "1.0.4"
|
||||
|
||||
"@img/sharp-wasm32@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1"
|
||||
integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==
|
||||
dependencies:
|
||||
"@emnapi/runtime" "^1.2.0"
|
||||
|
||||
"@img/sharp-win32-ia32@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9"
|
||||
integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==
|
||||
|
||||
"@img/sharp-win32-x64@0.33.5":
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342"
|
||||
integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.5":
|
||||
version "0.3.5"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36"
|
||||
|
@ -492,9 +596,9 @@
|
|||
integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==
|
||||
|
||||
"@noble/secp256k1@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-2.1.0.tgz#71d829a0b8ce84aea137708f82273977eea84597"
|
||||
integrity sha512-XLEQQNdablO0XZOIniFQimiXsZDNwaYgL96dZwC54Q30imSbAOFf3NKtepc+cXyuZf5Q1HCgbqgZ2UFFuHVcEw==
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-2.2.2.tgz#1dea2a2e63b9228db7148577cc7442b04669f5c8"
|
||||
integrity sha512-bOm9Lly0x58rMfj6rH2BccMtYU88uzh2bGw6mviScoaMQunnIiZffmnAL23lfuK+qkGRNTzvrWQPFzDzyRNaZw==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
|
@ -769,9 +873,9 @@
|
|||
integrity sha512-YaDrquRpZwfcXbnlDsSrBQNCChVOT9MGuSg+dMAyfsAa1SmiAhrA5jUYUiIMC59G92kIbY/AaQOWcBdq+lh+zg==
|
||||
|
||||
"@sveltejs/kit@^2.0.0":
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.15.0.tgz#9903e212a309a18ce9082ff6fe2cb64c3f4f79c1"
|
||||
integrity sha512-FI1bhfhFNGI2sKg+BhiRyM4eaOvX+KZqRYSQqL5PK3ZZREX2xufZ6MzZAw79N846OnIxYNqcz/3VOUq+FPDd3w==
|
||||
version "2.15.1"
|
||||
resolved "https://registry.yarnpkg.com/@sveltejs/kit/-/kit-2.15.1.tgz#66260c6175c5783138f4e43c5f14bd9cec28b914"
|
||||
integrity sha512-8t7D3hQHbUDMiaQ2RVnjJJ/+Ur4Fn/tkeySJCsHtX346Q9cp3LAnav8xXdfuqYNJwpUGX0x3BqF1uvbmXQw93A==
|
||||
dependencies:
|
||||
"@types/cookie" "^0.6.0"
|
||||
cookie "^0.6.0"
|
||||
|
@ -877,62 +981,62 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/swagger-ui/-/swagger-ui-3.52.4.tgz#96c4886e8f86ae392f8d940bf7029cf490a51c72"
|
||||
integrity sha512-7NV7q8BfupqdQxr26OkM0g0YEPB9uXnKGzXadgcearvI9MoCHt3F72lPTX3fZZIlrr21DC0IK26wcDMZ37oFDA==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@8.18.2", "@typescript-eslint/eslint-plugin@^8.7.0":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.2.tgz#c78e363ab5fe3b21dd1c90d8be9581534417f78e"
|
||||
integrity sha512-adig4SzPLjeQ0Tm+jvsozSGiCliI2ajeURDGHjZ2llnA+A67HihCQ+a3amtPhUakd1GlwHxSRvzOZktbEvhPPg==
|
||||
"@typescript-eslint/eslint-plugin@8.19.0", "@typescript-eslint/eslint-plugin@^8.7.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz#2b1e1b791e21d5fc27ddc93884db066444f597b5"
|
||||
integrity sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.18.2"
|
||||
"@typescript-eslint/type-utils" "8.18.2"
|
||||
"@typescript-eslint/utils" "8.18.2"
|
||||
"@typescript-eslint/visitor-keys" "8.18.2"
|
||||
"@typescript-eslint/scope-manager" "8.19.0"
|
||||
"@typescript-eslint/type-utils" "8.19.0"
|
||||
"@typescript-eslint/utils" "8.19.0"
|
||||
"@typescript-eslint/visitor-keys" "8.19.0"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.3.1"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/parser@8.18.2", "@typescript-eslint/parser@^8.7.0":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.18.2.tgz#0379a2e881d51d8fcf7ebdfa0dd18eee79182ce2"
|
||||
integrity sha512-y7tcq4StgxQD4mDr9+Jb26dZ+HTZ/SkfqpXSiqeUXZHxOUyjWDKsmwKhJ0/tApR08DgOhrFAoAhyB80/p3ViuA==
|
||||
"@typescript-eslint/parser@8.19.0", "@typescript-eslint/parser@^8.7.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.19.0.tgz#f1512e6e5c491b03aabb2718b95becde22b15292"
|
||||
integrity sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.18.2"
|
||||
"@typescript-eslint/types" "8.18.2"
|
||||
"@typescript-eslint/typescript-estree" "8.18.2"
|
||||
"@typescript-eslint/visitor-keys" "8.18.2"
|
||||
"@typescript-eslint/scope-manager" "8.19.0"
|
||||
"@typescript-eslint/types" "8.19.0"
|
||||
"@typescript-eslint/typescript-estree" "8.19.0"
|
||||
"@typescript-eslint/visitor-keys" "8.19.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.18.2.tgz#d193c200d61eb0ddec5987c8e48c9d4e1c0510bd"
|
||||
integrity sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==
|
||||
"@typescript-eslint/scope-manager@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz#28fa413a334f70e8b506a968531e0a7c9c3076dc"
|
||||
integrity sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.18.2"
|
||||
"@typescript-eslint/visitor-keys" "8.18.2"
|
||||
"@typescript-eslint/types" "8.19.0"
|
||||
"@typescript-eslint/visitor-keys" "8.19.0"
|
||||
|
||||
"@typescript-eslint/type-utils@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.18.2.tgz#5ad07e09002eee237591881df674c1c0c91ca52f"
|
||||
integrity sha512-AB/Wr1Lz31bzHfGm/jgbFR0VB0SML/hd2P1yxzKDM48YmP7vbyJNHRExUE/wZsQj2wUCvbWH8poNHFuxLqCTnA==
|
||||
"@typescript-eslint/type-utils@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz#41abd7d2e4cf93b6854b1fe6cbf416fab5abf89f"
|
||||
integrity sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "8.18.2"
|
||||
"@typescript-eslint/utils" "8.18.2"
|
||||
"@typescript-eslint/typescript-estree" "8.19.0"
|
||||
"@typescript-eslint/utils" "8.19.0"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/types@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.18.2.tgz#5ebad5b384c8aa1c0f86cee1c61bcdbe7511f547"
|
||||
integrity sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==
|
||||
"@typescript-eslint/types@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.19.0.tgz#a190a25c5484a42b81eaad06989579fdeb478cbb"
|
||||
integrity sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.2.tgz#fffb85527f8304e29bfbbdc712f4515da9f8b47c"
|
||||
integrity sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==
|
||||
"@typescript-eslint/typescript-estree@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz#6b4f48f98ffad6597379951b115710f4d68c9ccb"
|
||||
integrity sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.18.2"
|
||||
"@typescript-eslint/visitor-keys" "8.18.2"
|
||||
"@typescript-eslint/types" "8.19.0"
|
||||
"@typescript-eslint/visitor-keys" "8.19.0"
|
||||
debug "^4.3.4"
|
||||
fast-glob "^3.3.2"
|
||||
is-glob "^4.0.3"
|
||||
|
@ -940,22 +1044,22 @@
|
|||
semver "^7.6.0"
|
||||
ts-api-utils "^1.3.0"
|
||||
|
||||
"@typescript-eslint/utils@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.18.2.tgz#a2635f71904a84f9e47fe1b6f65a6d944ff1adf9"
|
||||
integrity sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==
|
||||
"@typescript-eslint/utils@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.19.0.tgz#33824310e1fccc17f27fbd1030fd8bbd9a674684"
|
||||
integrity sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "8.18.2"
|
||||
"@typescript-eslint/types" "8.18.2"
|
||||
"@typescript-eslint/typescript-estree" "8.18.2"
|
||||
"@typescript-eslint/scope-manager" "8.19.0"
|
||||
"@typescript-eslint/types" "8.19.0"
|
||||
"@typescript-eslint/typescript-estree" "8.19.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.18.2":
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.2.tgz#b3e434b701f086b10a7c82416ebc56899d27ef2f"
|
||||
integrity sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==
|
||||
"@typescript-eslint/visitor-keys@8.19.0":
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz#dc313f735e64c4979c9073f51ffcefb6d9be5c77"
|
||||
integrity sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.18.2"
|
||||
"@typescript-eslint/types" "8.19.0"
|
||||
eslint-visitor-keys "^4.2.0"
|
||||
|
||||
"@vitest/expect@2.1.8":
|
||||
|
@ -1116,15 +1220,6 @@ at-least-node@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
|
||||
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
|
||||
|
||||
axios@>=1.7.7:
|
||||
version "1.7.7"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
|
||||
integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
proxy-from-env "^1.1.0"
|
||||
|
||||
axobject-query@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee"
|
||||
|
@ -1277,11 +1372,27 @@ color-convert@^2.0.1:
|
|||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@~1.1.4:
|
||||
color-name@^1.0.0, color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-string@^1.9.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
|
||||
integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
|
||||
dependencies:
|
||||
color-name "^1.0.0"
|
||||
simple-swizzle "^0.2.2"
|
||||
|
||||
color@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
|
||||
integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
color-string "^1.9.0"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
|
@ -1421,6 +1532,11 @@ detect-libc@^1.0.3:
|
|||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||
|
||||
detect-libc@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
||||
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
|
||||
|
||||
devalue@^5.1.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/devalue/-/devalue-5.1.1.tgz#a71887ac0f354652851752654e4bd435a53891ae"
|
||||
|
@ -1837,11 +1953,6 @@ flatted@^3.2.9, flatted@^3.3.1:
|
|||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27"
|
||||
integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==
|
||||
|
||||
follow-redirects@^1.15.6:
|
||||
version "1.15.6"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
|
||||
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
|
@ -2070,6 +2181,11 @@ intl-messageformat@^10.5.3:
|
|||
"@formatjs/icu-messageformat-parser" "2.8.0"
|
||||
tslib "^2.7.0"
|
||||
|
||||
is-arrayish@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
|
||||
integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
|
||||
|
||||
is-docker@^2.0.0, is-docker@^2.1.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||
|
@ -2655,11 +2771,6 @@ pretty-format@^27.0.2:
|
|||
ansi-styles "^5.0.0"
|
||||
react-is "^17.0.1"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
punycode@^2.1.0, punycode@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||
|
@ -2716,12 +2827,12 @@ rimraf@^2.6.3:
|
|||
glob "^7.1.3"
|
||||
|
||||
rollup-plugin-visualizer@^5.12.0:
|
||||
version "5.12.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302"
|
||||
integrity sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ==
|
||||
version "5.13.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.13.1.tgz#fa49320cd23c8642f856f8bd45ddaf3e10a5b023"
|
||||
integrity sha512-vMg8i6BprL8aFm9DKvL2c8AwS8324EgymYQo9o6E26wgVvwMhsJxS37aNL6ZsU7X9iAcMYwdME7gItLfG5fwJg==
|
||||
dependencies:
|
||||
open "^8.4.0"
|
||||
picomatch "^2.3.1"
|
||||
picomatch "^4.0.2"
|
||||
source-map "^0.7.4"
|
||||
yargs "^17.5.1"
|
||||
|
||||
|
@ -2777,9 +2888,9 @@ sade@^1.7.4, sade@^1.8.1:
|
|||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sass@^1.79.3:
|
||||
version "1.83.0"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.0.tgz#e36842c0b88a94ed336fd16249b878a0541d536f"
|
||||
integrity sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw==
|
||||
version "1.83.1"
|
||||
resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.1.tgz#dee1ab94b47a6f9993d3195d36f556bcbda64846"
|
||||
integrity sha512-EVJbDaEs4Rr3F0glJzFSOvtg2/oy2V/YrGFPqPY24UqcLDWcI9ZY5sN+qyO3c/QCZwzgfirvhXvINiJCE/OLcA==
|
||||
dependencies:
|
||||
chokidar "^4.0.0"
|
||||
immutable "^5.0.2"
|
||||
|
@ -2799,7 +2910,7 @@ semver@^7.5.3:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
|
||||
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
|
||||
|
||||
semver@^7.5.4, semver@^7.6.0, semver@^7.6.2:
|
||||
semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
@ -2821,6 +2932,35 @@ set-function-length@^1.2.1:
|
|||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.2"
|
||||
|
||||
sharp@^0.33.5:
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
|
||||
integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==
|
||||
dependencies:
|
||||
color "^4.2.3"
|
||||
detect-libc "^2.0.3"
|
||||
semver "^7.6.3"
|
||||
optionalDependencies:
|
||||
"@img/sharp-darwin-arm64" "0.33.5"
|
||||
"@img/sharp-darwin-x64" "0.33.5"
|
||||
"@img/sharp-libvips-darwin-arm64" "1.0.4"
|
||||
"@img/sharp-libvips-darwin-x64" "1.0.4"
|
||||
"@img/sharp-libvips-linux-arm" "1.0.5"
|
||||
"@img/sharp-libvips-linux-arm64" "1.0.4"
|
||||
"@img/sharp-libvips-linux-s390x" "1.0.4"
|
||||
"@img/sharp-libvips-linux-x64" "1.0.4"
|
||||
"@img/sharp-libvips-linuxmusl-arm64" "1.0.4"
|
||||
"@img/sharp-libvips-linuxmusl-x64" "1.0.4"
|
||||
"@img/sharp-linux-arm" "0.33.5"
|
||||
"@img/sharp-linux-arm64" "0.33.5"
|
||||
"@img/sharp-linux-s390x" "0.33.5"
|
||||
"@img/sharp-linux-x64" "0.33.5"
|
||||
"@img/sharp-linuxmusl-arm64" "0.33.5"
|
||||
"@img/sharp-linuxmusl-x64" "0.33.5"
|
||||
"@img/sharp-wasm32" "0.33.5"
|
||||
"@img/sharp-win32-ia32" "0.33.5"
|
||||
"@img/sharp-win32-x64" "0.33.5"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
|
@ -2838,6 +2978,13 @@ siginfo@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
|
||||
integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
|
||||
|
||||
simple-swizzle@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
|
||||
integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
sirv@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-3.0.0.tgz#f8d90fc528f65dff04cb597a88609d4e8a4361ce"
|
||||
|
@ -3086,16 +3233,11 @@ ts-api-utils@^1.3.0:
|
|||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.3.tgz#bfc2215fe6528fecab2b0fba570a2e8a4263b064"
|
||||
integrity sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==
|
||||
|
||||
tslib@^2.7.0:
|
||||
tslib@^2.4.0, tslib@^2.7.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
|
||||
tunnel@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
|
||||
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
|
||||
|
@ -3109,24 +3251,19 @@ type@^2.7.2:
|
|||
integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==
|
||||
|
||||
typescript-eslint@^8.7.0:
|
||||
version "8.18.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.18.2.tgz#71334dcf843adc3fbb771dce5ade7876aa0d62b7"
|
||||
integrity sha512-KuXezG6jHkvC3MvizeXgupZzaG5wjhU3yE8E7e6viOvAvD9xAWYp8/vy0WULTGe9DYDWcQu7aW03YIV3mSitrQ==
|
||||
version "8.19.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.19.0.tgz#e4ff06b19f2f9807a2c26147a0199a109944d9e0"
|
||||
integrity sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.18.2"
|
||||
"@typescript-eslint/parser" "8.18.2"
|
||||
"@typescript-eslint/utils" "8.18.2"
|
||||
"@typescript-eslint/eslint-plugin" "8.19.0"
|
||||
"@typescript-eslint/parser" "8.19.0"
|
||||
"@typescript-eslint/utils" "8.19.0"
|
||||
|
||||
typescript@^5.5.4:
|
||||
version "5.6.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
|
||||
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
|
||||
|
||||
undici@>=5.28.4, undici@^5.25.4:
|
||||
version "6.19.8"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1"
|
||||
integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
|
@ -3144,11 +3281,6 @@ util-deprecate@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
vite-node@2.1.8:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-2.1.8.tgz#9495ca17652f6f7f95ca7c4b568a235e0c8dbac5"
|
||||
|
@ -3176,13 +3308,6 @@ vitefu@^0.2.5:
|
|||
resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969"
|
||||
integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==
|
||||
|
||||
vitest-github-actions-reporter@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/vitest-github-actions-reporter/-/vitest-github-actions-reporter-0.11.1.tgz#42ffaaf9d3b182cfb7552532f9d1e290190914e0"
|
||||
integrity sha512-ZHHB0wBgOPhMYCB17WKVlJZa+5SdudBZFoVoebwfq3ioIUTeLQGYHwh85vpdJAxRghLP8d0qI/6eCTueGyDVXA==
|
||||
dependencies:
|
||||
"@actions/core" "^1.10.0"
|
||||
|
||||
vitest@^2.1.1:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/vitest/-/vitest-2.1.8.tgz#2e6a00bc24833574d535c96d6602fb64163092fa"
|
||||
|
|
Loading…
Reference in a new issue