Add more consistency using the theme variables

This commit is contained in:
Arnaud 2026-02-20 15:50:53 +04:00
parent 2049492519
commit 9ac02311df
No known key found for this signature in database
GPG Key ID: 20E40A5D3110766F
10 changed files with 167 additions and 185 deletions

View File

@ -78,6 +78,41 @@ To restart the onboarding process, simply delete the prefences file and relaunch
The application also provides a JSON editor in the debug panel for runtime configuration tweaks. To apply changes, restart the Storage Module.
## Troubleshooting
### Node has no peers
**Symptom:**
The node starts successfully but never connects to any peer.
**Cause:**
Logos Storage uses a discovery port (default `8090`) to announce itself to the DHT and find peers. If this port is already use by another process, the DHT cannot work properly.
**Fix:**
Ensure that no process is running on `8090` process or change the default port value in the advanced configuration.
### UPnP not working
**Symptom:**
You selected UPnP during setup but the node remains unreachable.
**Cause:**
UPnP relies on your router supporting and enabling the UPnP protocol. Many routers have it disabled by default for security reasons.
**Fix:**
Make sure UPnP is enabled on your router or switch to port forwarding config.
### Manual port forwarding
**Symptom:**
You configure the port forwarding with a TCP port but the node remains unreachable.
**Cause:**
The port is not open on your router.
**Fix:**
Make port forwarding is enabled for this port on your router.
#### Nix Organization
The nix build system is organized into modular files in the `/nix` directory:

View File

@ -36,9 +36,9 @@ LogosStorageLayout {
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: "#1e1e1e"
color: Theme.palette.backgroundElevated
radius: 8
border.color: jsonArea.isValid ? "#3a3a3a" : "#ff0000"
border.color: jsonArea.isValid ? Theme.palette.borderSecondary : Theme.palette.error
border.width: 1
ScrollView {
@ -49,7 +49,7 @@ LogosStorageLayout {
id: jsonArea
font.family: "monospace"
font.pixelSize: 12
color: "#d4d4d4"
color: Theme.palette.text
wrapMode: Text.WrapAnywhere
background: Item {}
@ -85,29 +85,14 @@ LogosStorageLayout {
onClicked: root.back()
}
Rectangle {
width: 120
height: 36
radius: 8
color: jsonArea.isValid ? "#4CAF50" : "#444444"
Text {
anchors.centerIn: parent
text: "Validate"
color: "white"
font.pixelSize: 14
font.bold: true
}
MouseArea {
anchors.fill: parent
enabled: jsonArea.isValid
cursorShape: jsonArea.isValid ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: function () {
root.backend.saveUserConfig(jsonArea.text)
root.backend.reloadIfChanged(jsonArea.text)
root.completed()
}
LogosStorageButton {
text: "Validate"
variant: "success"
enabled: jsonArea.isValid
onClicked: {
root.backend.saveUserConfig(jsonArea.text)
root.backend.reloadIfChanged(jsonArea.text)
root.completed()
}
}
}

View File

@ -138,6 +138,12 @@ qt_add_qml_module(appqml
HealthIndicator.qml
ModeSelector.qml
AdvancedSetup.qml
DotIcon.qml
NodeStatusIcon.qml
GuideIcon.qml
AdvancedIcon.qml
UpnpIcon.qml
PortIcon.qml
)
# Set up QML module directory for runtime

View File

@ -1,4 +1,5 @@
import QtQuick
import Logos.Theme
Item {
id: root
@ -57,14 +58,14 @@ Item {
height: 10
radius: 5
anchors.verticalCenter: parent.verticalCenter
color: root.nodeIsUp ? "#4CAF50" : "#f44336"
color: root.nodeIsUp ? Theme.palette.success : Theme.palette.error
opacity: root.blinkOn ? 1.0 : 0.15
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: root.nodeIsUp ? "Node reachable" : "Node unreachable"
color: root.nodeIsUp ? "#4CAF50" : "#f44336"
color: root.nodeIsUp ? Theme.palette.success : Theme.palette.error
font.pixelSize: 12
}
}

View File

@ -6,10 +6,21 @@ Button {
id: control
padding: Theme.spacing.small
// "default" | "success"
property string variant: "default"
readonly property bool isSuccess: variant === "success"
contentItem: Text {
text: control.text
font.pixelSize: Theme.typography.primaryText
color: control.enabled ? Theme.palette.text : Theme.palette.textMuted
color: {
if (!control.enabled)
return Theme.palette.textMuted
if (control.isSuccess)
return Theme.palette.background
return Theme.palette.text
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
@ -18,10 +29,18 @@ Button {
color: {
if (!control.enabled)
return Theme.palette.backgroundElevated
if (control.isSuccess)
return Theme.palette.success
return Theme.palette.backgroundSecondary
}
border.width: 1
border.color: Theme.palette.border
border.color: {
if (!control.enabled)
return Theme.palette.border
if (control.isSuccess)
return Theme.palette.success
return Theme.palette.border
}
radius: Theme.spacing.tiny
Behavior on color {

View File

@ -8,7 +8,7 @@ LogosStorageLayout {
signal completed(bool isGuide)
property int selectedMode: -1 // 0 = guide, 1 = advanced
property int selectedMode: -1
ColumnLayout {
anchors.centerIn: parent
@ -30,9 +30,7 @@ LogosStorageLayout {
Layout.fillWidth: true
}
Item {
height: Theme.spacing.medium
}
Item { height: Theme.spacing.medium }
Row {
spacing: Theme.spacing.medium
@ -43,38 +41,22 @@ LogosStorageLayout {
width: 190
height: 230
radius: 14
color: root.selectedMode === 0 ? Qt.rgba(1, 1, 1,
0.08) : "transparent"
border.color: root.selectedMode === 0 ? "white" : Qt.rgba(1, 1,
1,
0.2)
color: root.selectedMode === 0 ? Theme.palette.overlayLight : "transparent"
border.color: root.selectedMode === 0 ? Theme.palette.text : Theme.palette.borderTertiaryMuted
border.width: root.selectedMode === 0 ? 2 : 1
ColumnLayout {
anchors.centerIn: parent
spacing: 14
// Nothing OS dot icon like
Grid {
columns: 5
spacing: 4
GuideIcon {
dotColor: Theme.palette.text
Layout.alignment: Qt.AlignHCenter
Repeater {
model: [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0]
Rectangle {
width: 6
height: 6
radius: 2
color: "white"
opacity: modelData ? 0.9 : 0.1
}
}
}
Text {
text: "Guide"
color: "white"
color: Theme.palette.text
font.pixelSize: 16
font.bold: true
Layout.alignment: Qt.AlignHCenter
@ -82,7 +64,7 @@ LogosStorageLayout {
Text {
text: "Step-by-step setup.\nRecommended for\nmost users."
color: Qt.rgba(1, 1, 1, 0.55)
color: Theme.palette.textSecondary
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
@ -103,38 +85,22 @@ LogosStorageLayout {
width: 190
height: 230
radius: 14
color: root.selectedMode === 1 ? Qt.rgba(1, 1, 1,
0.08) : "transparent"
border.color: root.selectedMode === 1 ? "white" : Qt.rgba(1, 1,
1,
0.2)
color: root.selectedMode === 1 ? Theme.palette.overlayLight : "transparent"
border.color: root.selectedMode === 1 ? Theme.palette.text : Theme.palette.borderTertiaryMuted
border.width: root.selectedMode === 1 ? 2 : 1
ColumnLayout {
anchors.centerIn: parent
spacing: 14
// Nothing OS dot icon like
Grid {
columns: 5
spacing: 4
AdvancedIcon {
dotColor: Theme.palette.text
Layout.alignment: Qt.AlignHCenter
Repeater {
model: [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1]
Rectangle {
width: 6
height: 6
radius: 2
color: "white"
opacity: modelData ? 0.9 : 0.1
}
}
}
Text {
text: "Advanced"
color: "white"
color: Theme.palette.text
font.pixelSize: 16
font.bold: true
Layout.alignment: Qt.AlignHCenter
@ -142,7 +108,7 @@ LogosStorageLayout {
Text {
text: "Manual JSON\nconfiguration for\nexperienced users."
color: Qt.rgba(1, 1, 1, 0.55)
color: Theme.palette.textSecondary
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
@ -159,9 +125,7 @@ LogosStorageLayout {
}
}
Item {
height: Theme.spacing.small
}
Item { height: Theme.spacing.small }
LogosStorageButton {
text: "Continue"

View File

@ -11,7 +11,7 @@ LogosStorageLayout {
signal back
signal completed(bool upnpEnabled)
property int selectedMode: -1 // 0 = upnp, 1 = port forwarding
property int selectedMode: -1
ColumnLayout {
anchors.centerIn: parent
@ -33,9 +33,7 @@ LogosStorageLayout {
Layout.fillWidth: true
}
Item {
height: Theme.spacing.medium
}
Item { height: Theme.spacing.medium }
Row {
spacing: Theme.spacing.medium
@ -46,41 +44,22 @@ LogosStorageLayout {
width: 190
height: 230
radius: 14
color: root.selectedMode === 0 ? Qt.rgba(1, 1, 1, 0.08) : "transparent"
border.color: root.selectedMode === 0 ? "white" : Qt.rgba(1, 1, 1, 0.2)
color: root.selectedMode === 0 ? Theme.palette.overlayLight : "transparent"
border.color: root.selectedMode === 0 ? Theme.palette.text : Theme.palette.borderTertiaryMuted
border.width: root.selectedMode === 0 ? 2 : 1
ColumnLayout {
anchors.centerIn: parent
spacing: 14
// Nothing OS dot icon diamond/network
Grid {
columns: 5
spacing: 4
UpnpIcon {
dotColor: Theme.palette.text
Layout.alignment: Qt.AlignHCenter
Repeater {
model: [
0, 0, 1, 0, 0,
0, 1, 0, 1, 0,
1, 0, 1, 0, 1,
0, 1, 0, 1, 0,
0, 0, 1, 0, 0
]
Rectangle {
width: 6
height: 6
radius: 2
color: "white"
opacity: modelData ? 0.9 : 0.1
}
}
}
Text {
text: "UPnP"
color: "white"
color: Theme.palette.text
font.pixelSize: 16
font.bold: true
Layout.alignment: Qt.AlignHCenter
@ -88,7 +67,7 @@ LogosStorageLayout {
Text {
text: "Automatic port\nforwarding via\nUPnP router."
color: Qt.rgba(1, 1, 1, 0.55)
color: Theme.palette.textSecondary
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
@ -109,41 +88,22 @@ LogosStorageLayout {
width: 190
height: 230
radius: 14
color: root.selectedMode === 1 ? Qt.rgba(1, 1, 1, 0.08) : "transparent"
border.color: root.selectedMode === 1 ? "white" : Qt.rgba(1, 1, 1, 0.2)
color: root.selectedMode === 1 ? Theme.palette.overlayLight : "transparent"
border.color: root.selectedMode === 1 ? Theme.palette.text : Theme.palette.borderTertiaryMuted
border.width: root.selectedMode === 1 ? 2 : 1
ColumnLayout {
anchors.centerIn: parent
spacing: 14
// Nothing OS dot icon right arrow
Grid {
columns: 5
spacing: 4
PortIcon {
dotColor: Theme.palette.text
Layout.alignment: Qt.AlignHCenter
Repeater {
model: [
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
1, 1, 1, 1, 1,
0, 0, 0, 1, 0,
0, 0, 1, 0, 0
]
Rectangle {
width: 6
height: 6
radius: 2
color: "white"
opacity: modelData ? 0.9 : 0.1
}
}
}
Text {
text: "Port Forwarding"
color: "white"
color: Theme.palette.text
font.pixelSize: 16
font.bold: true
Layout.alignment: Qt.AlignHCenter
@ -151,7 +111,7 @@ LogosStorageLayout {
Text {
text: "Manual TCP port\nconfiguration on\nyour router."
color: Qt.rgba(1, 1, 1, 0.55)
color: Theme.palette.textSecondary
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
@ -168,9 +128,7 @@ LogosStorageLayout {
}
}
Item {
height: Theme.spacing.small
}
Item { height: Theme.spacing.small }
RowLayout {
Layout.alignment: Qt.AlignHCenter

View File

@ -16,10 +16,6 @@ LogosStorageLayout {
Connections {
target: root.backend
// The nat ext checking needs a bit of
// time because the Storage backend retrieves
// the public IP by making a call to the echo service.
// When the config is done, just push the startNodeComponent.
function onNatExtConfigCompleted() {
root.loading = false
root.completed(root.tcpPort)
@ -30,20 +26,26 @@ LogosStorageLayout {
anchors.centerIn: parent
spacing: Theme.spacing.medium
width: 400
Layout.fillWidth: true
LogosText {
id: questionText
font.pixelSize: Theme.typography.titleText
text: "Choose your TCP port"
Layout.alignment: Qt.AlignCenter
PortIcon {
animated: root.loading
dotColor: Theme.palette.text
Layout.alignment: Qt.AlignHCenter
}
LogosText {
font.pixelSize: Theme.typography.titleText
text: "Port Configuration"
Layout.alignment: Qt.AlignHCenter
}
LogosText {
id: questionDescriptionText
font.pixelSize: Theme.typography.primaryText
text: "The TCP port has to be open to connect with other remote peers."
Layout.alignment: Qt.AlignCenter
text: "The TCP port must be open to connect with remote peers."
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
LogosTextField {
@ -64,14 +66,14 @@ LogosStorageLayout {
}
}
Row {
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: Theme.spacing.small
LogosStorageButton {
text: "Back"
onClicked: {
root.back()
}
enabled: !root.loading
onClicked: root.back()
}
LogosStorageButton {
@ -83,6 +85,14 @@ LogosStorageLayout {
}
}
}
LogosText {
font.pixelSize: Theme.typography.primaryText
text: "Retrieving your public IP..."
color: Theme.palette.textTertiary
visible: root.loading
Layout.alignment: Qt.AlignHCenter
}
}
QtObject {

View File

@ -9,7 +9,7 @@ LogosStorageLayout {
property var backend: mockBackend
property string status: ""
property string title: "Starting your node...."
property string title: "Starting your node"
property string resolution: ""
property bool starting: true
property bool success: false
@ -19,15 +19,13 @@ LogosStorageLayout {
function onNodeStarted() {
root.starting = false
root.status = "Logos Storage started successfully."
root.title = "Success"
root.status = "Your node is up and reachable."
root.title = "Node is ready"
root.success = true
}
Component.onCompleted: root.backend.start()
// Wait after startCompleted before calling checkNodeIs to
// make sure the the node is started and ready.
Timer {
id: nodeCheckTimer
interval: 500
@ -39,9 +37,8 @@ LogosStorageLayout {
target: root.backend
function onStartCompleted() {
console.info("startCompleted")
root.title = "Checking.."
root.status = "Your node is started, checking everything is up."
root.title = "Checking connectivity"
root.status = "Node started, verifying reachability..."
nodeCheckTimer.start()
}
@ -57,7 +54,7 @@ LogosStorageLayout {
function onNodeIsntUp(reason) {
root.starting = false
root.title = "Node not reachable"
root.title = "Node unreachable"
root.status = ""
root.resolution = reason
}
@ -69,28 +66,35 @@ LogosStorageLayout {
spacing: Theme.spacing.medium
LogosText {
id: titleText
font.pixelSize: Theme.typography.titleText
text: root.title
Layout.alignment: Qt.AlignHCenter
}
LogosText {
id: statusText
font.pixelSize: Theme.typography.primaryText
text: root.status
NodeStatusIcon {
starting: root.starting
success: root.success
Layout.alignment: Qt.AlignHCenter
wrapMode: Text.WordWrap
}
LogosText {
id: resolutionText
font.pixelSize: Theme.typography.primaryText
text: root.status
visible: root.status !== ""
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
LogosText {
font.pixelSize: Theme.typography.primaryText
text: root.resolution
visible: root.resolution !== ""
color: Theme.palette.error
wrapMode: Text.WordWrap
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
}
}
@ -101,12 +105,11 @@ LogosStorageLayout {
anchors.bottomMargin: 10
anchors.leftMargin: 10
text: "Back"
onClicked: function () {
enabled: !root.starting
onClicked: {
root.backend.stop()
root.back()
}
enabled: root.starting == false
}
LogosStorageButton {
@ -115,40 +118,33 @@ LogosStorageLayout {
anchors.bottomMargin: 10
anchors.rightMargin: 10
text: "Next"
onClicked: function () {
enabled: root.success
onClicked: {
root.backend.saveCurrentConfig()
root.next()
}
enabled: root.success == true
}
// In preview/mock mode, simulate a successful node start after 2 seconds
Timer {
interval: 2000
running: root.backend && root.backend.isMock === true
onTriggered: root.onNodeStarted()
repeat: false
onTriggered: root.onNodeStarted()
}
QtObject {
id: mockBackend
readonly property bool isMock: true
property string configJson: "{}"
signal startCompleted
signal startFailed(string error)
signal nodeIsUp
signal nodeIsntUp(string reason)
function guessResolution() {
return ""
}
function checkNodeIsUp() {}
function stop() {}
function saveCurrentConfig() {}
function start() {}
}
}

View File

@ -10,5 +10,13 @@
<file alias="PortForwarding.qml">qml/PortForwarding.qml</file>
<file alias="ErrorToast.qml">qml/ErrorToast.qml</file>
<file alias="HealthIndicator.qml">qml/HealthIndicator.qml</file>
<file alias="ModeSelector.qml">qml/ModeSelector.qml</file>
<file alias="AdvancedSetup.qml">qml/AdvancedSetup.qml</file>
<file alias="DotIcon.qml">qml/DotIcon.qml</file>
<file alias="NodeStatusIcon.qml">qml/NodeStatusIcon.qml</file>
<file alias="GuideIcon.qml">qml/GuideIcon.qml</file>
<file alias="AdvancedIcon.qml">qml/AdvancedIcon.qml</file>
<file alias="UpnpIcon.qml">qml/UpnpIcon.qml</file>
<file alias="PortIcon.qml">qml/PortIcon.qml</file>
</qresource>
</RCC>