Merge pull request #8 from logos-co/feat/add-table

feat: add table
This commit is contained in:
Arnaud 2026-02-17 13:09:36 +04:00 committed by GitHub
commit da1621df7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 545 additions and 149 deletions

View File

@ -66,6 +66,8 @@ LogosResult StorageBackend::init(const QString& configJson = "{}") {
} else {
setStatus(Running);
debug("Storage module started.");
QMetaObject::invokeMethod(this, &StorageBackend::downloadManifests, Qt::QueuedConnection);
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
emit startCompleted();
}
})) {
@ -155,6 +157,8 @@ LogosResult StorageBackend::init(const QString& configJson = "{}") {
m_uploadStatus = "Upload completed!";
emit uploadProgressChanged();
emit uploadStatusChanged();
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
}
})) {
qWarning() << "StorageWidget: failed to subscribe to storageUploadProgress events";
@ -549,11 +553,22 @@ void StorageBackend::remove(const QString& cid) {
LogosResult result = m_logos->storage_module.remove(cid);
if (!result.success) {
debug("StorageBackend::remove failed with error=" + result.getError());
return;
// Log but continue — manifest might not have local data, remove it from the list anyway
debug("StorageBackend::remove: storage returned error=" + result.getError() + " (removing from list regardless)");
} else {
debug("Cid " + cid + " removed from storage.");
}
debug("Cid " + cid + " removed.");
// Always remove from manifests list
for (int i = 0; i < m_manifests.size(); ++i) {
if (m_manifests[i].toMap().value("cid").toString() == cid) {
m_manifests.removeAt(i);
emit manifestsChanged();
break;
}
}
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
}
void StorageBackend::fetch(const QString& cid) {
@ -631,18 +646,38 @@ void StorageBackend::downloadManifest(const QString& cid) {
return;
}
debug("Manifest tree cid: " + result.getString("treeCid"));
debug(QString("Manifest datasetSize %1").arg(result.getInt("datasetSize")));
debug(QString("Manifest blockSize %1").arg(result.getInt("blockSize")));
debug("Manifest filename: " + result.getString("filename"));
debug("Manifest mimetype: " + result.getString("mimetype"));
QString treeCid = result.getString("treeCid");
qint64 datasetSize = result.getInt("datasetSize");
qint64 blockSize = result.getInt("blockSize");
QString filename = result.getString("filename");
QString mimetype = result.getString("mimetype");
debug("Manifest tree cid: " + treeCid);
debug(QString("Manifest datasetSize %1").arg(datasetSize));
debug(QString("Manifest blockSize %1").arg(blockSize));
debug("Manifest filename: " + filename);
debug("Manifest mimetype: " + mimetype);
// Add to manifests list
QVariantMap manifest;
manifest["cid"] = cid;
manifest["treeCid"] = treeCid;
manifest["filename"] = filename;
manifest["mimetype"] = mimetype;
manifest["datasetSize"] = datasetSize;
manifest["blockSize"] = blockSize;
m_manifests.append(manifest);
emit manifestsChanged();
}
QVariantList StorageBackend::manifests() const { return m_manifests; }
void StorageBackend::downloadManifests() {
qDebug() << "StorageBackend::downloadManifests called";
LogosResult result = m_logos->storage_module.manifests();
QString error = result.getError();
if (!result.success) {
debug("StorageBackend::downloadManifests failed with error=" + result.getError());
return;
@ -650,22 +685,25 @@ void StorageBackend::downloadManifests() {
QVariantList manifestsList = result.getList();
int count = manifestsList.size();
debug(QString("Found %1 manifests").arg(count));
// for (const QVariant& manifestVariant : manifestsList) {
// QVariantMap manifest = manifestVariant.toMap();
m_manifests.clear();
// QString cid = manifest["cid"].toString();
// QString treeCid = manifest["treeCid"].toString();
// QString filename = manifest["filename"].toString();
// qint64 datasetSize = manifest["datasetSize"].toLongLong();
for (const QVariant& manifestVariant : manifestsList) {
QVariantMap src = manifestVariant.toMap();
// debug(QString("Manifest: %1, treeCid: %2, size: %3")
// .arg(filename)
// .arg(treeCid.isEmpty() ? "EMPTY" : treeCid)
// .arg(datasetSize));
// }
QVariantMap manifest;
manifest["cid"] = src.value("cid").toString();
manifest["treeCid"] = src.value("treeCid").toString();
manifest["filename"] = src.value("filename").toString();
manifest["mimetype"] = src.value("mimetype").toString();
manifest["datasetSize"] = src.value("datasetSize").toLongLong();
manifest["blockSize"] = src.value("blockSize").toLongLong();
m_manifests.append(manifest);
}
emit manifestsChanged();
}
void StorageBackend::space() {
@ -678,12 +716,33 @@ void StorageBackend::space() {
return;
}
debug(QString("Space datasetSize %1").arg(result.getInt("totalBlocks")));
debug(QString("Space quotaMaxBytes %1").arg(result.getInt("quotaMaxBytes")));
debug(QString("Space quotaUsedBytes %1").arg(result.getInt("quotaUsedBytes")));
debug(QString("Space quotaReservedBytes %1").arg(result.getInt("quotaReservedBytes")));
qDebug() << "StorageBackend::space raw value:" << result.value;
static constexpr qint64 DEFAULT_QUOTA = 20LL * 1024 * 1024 * 1024; // 20 GB
// Check config for a quota-max-bytes override
qint64 configQuota = 0;
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
if (!doc.isNull()) {
configQuota = doc.object().value("quota-max-bytes").toVariant().toLongLong();
}
qint64 apiQuota = result.getInt("quotaMaxBytes");
m_quotaMaxBytes = apiQuota > 0 ? apiQuota : (configQuota > 0 ? configQuota : DEFAULT_QUOTA);
m_quotaUsedBytes = result.getInt("quotaUsedBytes");
m_quotaReservedBytes = result.getInt("quotaReservedBytes");
emit quotaChanged();
debug(QString("Space totalBlocks %1").arg(result.getInt("totalBlocks")));
debug(QString("Space quotaMaxBytes %1").arg(m_quotaMaxBytes));
debug(QString("Space quotaUsedBytes %1").arg(m_quotaUsedBytes));
debug(QString("Space quotaReservedBytes %1").arg(m_quotaReservedBytes));
}
qint64 StorageBackend::quotaMaxBytes() const { return m_quotaMaxBytes; }
qint64 StorageBackend::quotaUsedBytes() const { return m_quotaUsedBytes; }
qint64 StorageBackend::quotaReservedBytes() const { return m_quotaReservedBytes; }
void StorageBackend::updateLogLevel(const QString& logLevel) {
qDebug() << "StorageBackend::updateLogLevel called with logLevel=" << logLevel;

View File

@ -33,6 +33,10 @@ class StorageBackend : public QObject {
Q_PROPERTY(QString configJson READ configJson NOTIFY configJsonChanged)
Q_PROPERTY(int uploadProgress READ uploadProgress NOTIFY uploadProgressChanged)
Q_PROPERTY(QString uploadStatus READ uploadStatus NOTIFY uploadStatusChanged)
Q_PROPERTY(QVariantList manifests READ manifests NOTIFY manifestsChanged)
Q_PROPERTY(qint64 quotaMaxBytes READ quotaMaxBytes NOTIFY quotaChanged)
Q_PROPERTY(qint64 quotaUsedBytes READ quotaUsedBytes NOTIFY quotaChanged)
Q_PROPERTY(qint64 quotaReservedBytes READ quotaReservedBytes NOTIFY quotaChanged)
public:
enum StorageStatus { Stopped = 0, Starting, Running, Stopping, Destroyed };
@ -44,6 +48,10 @@ class StorageBackend : public QObject {
QString configJson() const;
int uploadProgress() const;
QString uploadStatus() const;
QVariantList manifests() const;
qint64 quotaMaxBytes() const;
qint64 quotaUsedBytes() const;
qint64 quotaReservedBytes() const;
Q_INVOKABLE static QString defaultDataDir();
@ -88,6 +96,8 @@ class StorageBackend : public QObject {
void configJsonChanged();
void uploadProgressChanged();
void uploadStatusChanged();
void manifestsChanged();
void quotaChanged();
private slots:
@ -106,4 +116,8 @@ class StorageBackend : public QObject {
QString m_uploadStatus = "";
qint64 m_uploadTotalBytes = 0;
qint64 m_uploadedBytes = 0;
QVariantList m_manifests;
qint64 m_quotaMaxBytes = 0;
qint64 m_quotaUsedBytes = 0;
qint64 m_quotaReservedBytes = 0;
};

View File

@ -2,13 +2,14 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Dialogs
import QtQuick.Layouts
import QtCore
Rectangle {
id: root
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: 600
implicitHeight: 400
implicitHeight: 600
color: "#000000"
property var backend: mockBackend
@ -22,6 +23,7 @@ Rectangle {
property url downloadCid: ""
property string logLevel: ""
property bool showDebug: false
property var pendingDownloadManifest: null
property url uploadCid: root.backend.cid
property url configJson: root.backend.configJson
@ -124,6 +126,19 @@ Rectangle {
function space() {}
function updateLogLevel(logLevel) {}
property var manifests: []
property var quotaMaxBytes: 20 * 1024 * 1024 * 1024 // 20 GB default
property var quotaUsedBytes: 0
property var quotaReservedBytes: 0
}
function formatBytes(bytes) {
if (bytes <= 0) return "0 B"
if (bytes < 1024) return bytes + " B"
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB"
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + " MB"
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB"
}
Text {
@ -213,36 +228,35 @@ Rectangle {
}
}
TextField {
id: peerIdField
placeholderText: "Enter the peer Id"
placeholderTextColor: "#999999"
color: "#000000"
selectByMouse: true
text: root.peerId
onTextChanged: root.peerId = text
anchors.top: uploadProgressColumn.bottom
anchors.topMargin: 50
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
id: peerConnectButton
objectName: "peerConnectButton"
text: "Peer connect"
onClicked: root.backend.tryPeerConnect(root.peerId)
anchors.top: peerIdField.bottom
anchors.horizontalCenter: parent.horizontalCenter
enabled: root.isRunning
anchors.topMargin: 10
}
// TextField {
// id: peerIdField
// placeholderText: "Enter the peer Id"
// placeholderTextColor: "#999999"
// color: "#000000"
// selectByMouse: true
// text: root.peerId
// onTextChanged: root.peerId = text
// anchors.top: uploadProgressColumn.bottom
// anchors.topMargin: 50
// anchors.horizontalCenter: parent.horizontalCenter
// }
// Button {
// id: peerConnectButton
// objectName: "peerConnectButton"
// text: "Peer connect"
// onClicked: root.backend.tryPeerConnect(root.peerId)
// anchors.top: peerIdField.bottom
// anchors.horizontalCenter: parent.horizontalCenter
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
id: debugButton
objectName: "debugButton"
text: "Debug"
onClicked: root.backend.tryDebug()
anchors.top: peerConnectButton.bottom
anchors.top: uploadProgressColumn.bottom
anchors.horizontalCenter: parent.horizontalCenter
enabled: root.isRunning
anchors.topMargin: 50
@ -253,7 +267,7 @@ Rectangle {
objectName: "peerIdButton"
text: "Peer Id"
onClicked: root.backend.showPeerId()
anchors.top: peerConnectButton.bottom
anchors.top: uploadProgressColumn.bottom
anchors.right: debugButton.left
enabled: root.isRunning
anchors.topMargin: 50
@ -264,7 +278,7 @@ Rectangle {
objectName: "dataDirButton"
text: "Data dir"
onClicked: root.backend.dataDir()
anchors.top: peerConnectButton.bottom
anchors.top: uploadProgressColumn.bottom
anchors.right: peerIdButton.left
enabled: root.isRunning
anchors.topMargin: 50
@ -275,7 +289,7 @@ Rectangle {
objectName: "sprButton"
text: "SPR"
onClicked: root.backend.spr()
anchors.top: peerConnectButton.bottom
anchors.top: uploadProgressColumn.bottom
anchors.left: debugButton.right
enabled: root.isRunning
anchors.topMargin: 50
@ -286,91 +300,92 @@ Rectangle {
objectName: "versionButton"
text: "Version"
onClicked: root.backend.version()
anchors.top: peerConnectButton.bottom
anchors.top: uploadProgressColumn.bottom
anchors.left: sprButton.right
enabled: root.isRunning
anchors.topMargin: 50
}
TextField {
id: cidDownloadField
placeholderTextColor: "#999999"
placeholderText: "Enter the cid to download"
color: "black"
// text: root.downloadCid
onTextChanged: root.downloadCid = text
anchors.top: debugButton.bottom
anchors.topMargin: 50
anchors.horizontalCenter: parent.horizontalCenter
}
// TextField {
// id: cidDownloadField
// placeholderTextColor: "#999999"
// placeholderText: "Enter the cid to download"
// color: "black"
// // text: root.downloadCid
// onTextChanged: root.downloadCid = text
// anchors.top: debugButton.bottom
// anchors.topMargin: 50
// anchors.horizontalCenter: parent.horizontalCenter
// }
Button {
id: openFile2
text: "Open file"
onClicked: fileDialog2.open()
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: cidDownloadField.bottom
anchors.topMargin: 15
enabled: root.isRunning
}
// Button {
// id: openFile2
// text: "Open file"
// onClicked: fileDialog2.open()
// anchors.horizontalCenter: parent.horizontalCenter
// anchors.top: cidDownloadField.bottom
// anchors.topMargin: 15
// enabled: root.isRunning
// }
Button {
id: cidDownloadButton
objectName: "cidDownloadButton"
text: "Download"
onClicked: root.backend.tryDownloadFile(root.downloadCid,
root.downloadDestination)
anchors.top: openFile2.bottom
anchors.horizontalCenter: parent.horizontalCenter
enabled: root.isRunning
anchors.topMargin: 10
}
// Button {
// id: cidDownloadButton
// objectName: "cidDownloadButton"
// text: "Download"
// onClicked: root.backend.tryDownloadFile(root.downloadCid,
// root.downloadDestination)
// anchors.top: openFile2.bottom
// anchors.horizontalCenter: parent.horizontalCenter
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
id: existsButton
objectName: "existsButton"
text: "Exists"
onClicked: root.backend.exists(root.downloadCid)
anchors.top: openFile2.bottom
anchors.left: cidDownloadButton.right
enabled: root.isRunning
anchors.topMargin: 10
}
// Button {
// id: existsButton
// objectName: "existsButton"
// text: "Exists"
// onClicked: root.backend.exists(root.downloadCid)
// anchors.top: openFile2.bottom
// anchors.left: cidDownloadButton.right
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
id: fetchButton
objectName: "fetchButton"
text: "Fetch"
onClicked: root.backend.fetch(root.downloadCid)
anchors.top: openFile2.bottom
anchors.left: existsButton.right
enabled: root.isRunning
anchors.topMargin: 10
}
// Button {
// id: fetchButton
// objectName: "fetchButton"
// text: "Fetch"
// onClicked: root.backend.fetch(root.downloadCid)
// anchors.top: openFile2.bottom
// anchors.left: existsButton.right
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
id: removeButton
objectName: "removeButton"
text: "Remove"
onClicked: root.backend.remove(root.downloadCid)
anchors.top: openFile2.bottom
anchors.right: cidDownloadButton.left
enabled: root.isRunning
anchors.topMargin: 10
}
// Button {
// id: removeButton
// objectName: "removeButton"
// text: "Remove"
// onClicked: root.backend.remove(root.downloadCid)
// anchors.top: openFile2.bottom
// anchors.right: cidDownloadButton.left
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
id: downloadManifestButton
objectName: "downloadManifestButton"
text: "Download manifest"
onClicked: root.backend.downloadManifest(root.downloadCid)
anchors.top: openFile2.bottom
anchors.right: removeButton.left
enabled: root.isRunning
anchors.topMargin: 10
}
// Button {
// id: downloadManifestButton
// objectName: "downloadManifestButton"
// text: "Download manifest"
// onClicked: root.backend.downloadManifest(root.downloadCid)
// anchors.top: openFile2.bottom
// anchors.right: removeButton.left
// enabled: root.isRunning
// anchors.topMargin: 10
// }
Button {
/*Button {
id: downloadManifestsButton
objectName: "downloadManifestsButton"
text: "Manifests"
@ -379,6 +394,297 @@ Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
enabled: root.isRunning
anchors.topMargin: 10
}*/
// Manifests section
Text {
id: manifestsTitle
text: "Manifests"
color: "white"
font.pixelSize: 14
font.bold: true
anchors.top: versionButton.bottom
anchors.topMargin: 30
anchors.horizontalCenter: parent.horizontalCenter
}
// Disk space bar
Item {
id: spaceBarSection
anchors.top: manifestsTitle.bottom
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 40
height: root.backend.quotaMaxBytes > 0 ? 36 : 20
readonly property real total: root.backend.quotaMaxBytes
readonly property real used: root.backend.quotaUsedBytes
readonly property real reserved: root.backend.quotaReservedBytes
// No quota configured
Text {
anchors.centerIn: parent
text: "No quota configured"
color: "#555555"
font.pixelSize: 11
visible: spaceBarSection.total <= 0
}
// Background track
Rectangle {
id: spaceBarTrack
visible: spaceBarSection.total > 0
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
height: 14
radius: 7
color: "#2a2a2a"
border.color: "#3a3a3a"
border.width: 1
clip: true
// Used (green)
Rectangle {
width: Math.min(parent.width * (spaceBarSection.used / spaceBarSection.total), parent.width)
height: parent.height
radius: parent.radius
color: "#4CAF50"
}
// Reserved (orange), stacked after used
Rectangle {
x: parent.width * (spaceBarSection.used / spaceBarSection.total)
width: Math.min(parent.width * (spaceBarSection.reserved / spaceBarSection.total),
parent.width - x)
height: parent.height
color: "#FF9800"
}
}
// Labels
Row {
visible: spaceBarSection.total > 0
anchors.top: spaceBarTrack.bottom
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
spacing: 16
Text {
text: "Used: " + root.formatBytes(spaceBarSection.used)
color: "#4CAF50"
font.pixelSize: 10
}
Text {
text: "Reserved: " + root.formatBytes(spaceBarSection.reserved)
color: "#FF9800"
font.pixelSize: 10
}
Text {
text: "Free: " + root.formatBytes(spaceBarSection.total - spaceBarSection.used - spaceBarSection.reserved)
color: "#888888"
font.pixelSize: 10
}
Text {
text: "Total: " + root.formatBytes(spaceBarSection.total)
color: "#555555"
font.pixelSize: 10
}
}
}
Row {
id: manifestInputRow
spacing: 8
anchors.top: spaceBarSection.bottom
anchors.topMargin: 16
anchors.horizontalCenter: parent.horizontalCenter
TextField {
id: manifestCidField
width: 380
placeholderText: "Enter CID to download manifest"
placeholderTextColor: "#999999"
color: "#000000"
selectByMouse: true
}
Button {
id: addManifestButton
text: "Download Manifest"
enabled: root.isRunning() && manifestCidField.text.length > 0
onClicked: {
root.backend.downloadManifest(manifestCidField.text)
manifestCidField.clear()
}
}
}
// Table header
Rectangle {
id: manifestTableHeader
anchors.top: manifestInputRow.bottom
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width - 40
height: 28
color: "#222222"
radius: 2
Row {
anchors.fill: parent
anchors.leftMargin: 6
Text {
width: 150
text: "CID"
color: "#aaaaaa"
font.pixelSize: 11
font.bold: true
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 120
text: "Filename"
color: "#aaaaaa"
font.pixelSize: 11
font.bold: true
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 85
text: "MIME type"
color: "#aaaaaa"
font.pixelSize: 11
font.bold: true
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 75
text: "Size (bytes)"
color: "#aaaaaa"
font.pixelSize: 11
font.bold: true
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 110
text: ""
color: "#aaaaaa"
font.pixelSize: 11
font.bold: true
anchors.verticalCenter: parent.verticalCenter
}
}
}
Rectangle {
id: manifestTableContainer
anchors.top: manifestTableHeader.bottom
anchors.left: manifestTableHeader.left
anchors.right: manifestTableHeader.right
height: 280
color: "#111111"
border.color: "#333333"
border.width: 1
clip: true
ListView {
id: manifestListView
anchors.fill: parent
model: root.backend.manifests
clip: true
delegate: Rectangle {
width: manifestListView.width
height: 36
color: index % 2 === 0 ? "#181818" : "#1e1e1e"
Row {
anchors.fill: parent
anchors.leftMargin: 6
anchors.rightMargin: 4
spacing: 0
Text {
width: 150
text: modelData["cid"] ?? ""
color: "#dddddd"
font.pixelSize: 11
font.family: "monospace"
elide: Text.ElideMiddle
anchors.verticalCenter: parent.verticalCenter
ToolTip.visible: hovered
ToolTip.text: modelData["cid"] ?? ""
HoverHandler {}
}
Text {
width: 120
text: modelData["filename"] ?? ""
color: "#dddddd"
font.pixelSize: 11
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 85
text: modelData["mimetype"] ?? ""
color: "#dddddd"
font.pixelSize: 11
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Text {
width: 75
text: modelData["datasetSize"] ?? ""
color: "#dddddd"
font.pixelSize: 11
elide: Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
}
Row {
spacing: 4
anchors.verticalCenter: parent.verticalCenter
Button {
width: 50
height: 26
text: "↓"
enabled: root.isRunning()
onClicked: {
root.pendingDownloadManifest = modelData
var filename = modelData["filename"]
|| modelData["cid"] || "download"
manifestSaveDialog.currentFile = StandardPaths.writableLocation(
StandardPaths.HomeLocation) + "/" + filename
manifestSaveDialog.open()
}
}
Button {
width: 50
height: 26
text: "🗑"
enabled: root.isRunning()
onClicked: root.backend.remove(
modelData["cid"] ?? "")
}
}
}
}
Text {
anchors.centerIn: parent
text: "No manifests yet"
color: "#555555"
font.pixelSize: 12
visible: manifestListView.count === 0
}
}
}
Button {
@ -386,34 +692,35 @@ Rectangle {
objectName: "spaceButton"
text: "Space"
onClicked: root.backend.space()
anchors.top: cidDownloadButton.bottom
anchors.right: downloadManifestsButton.left
anchors.top: manifestTableContainer.bottom
enabled: root.isRunning
anchors.topMargin: 10
}
TextField {
id: logLevelField
placeholderTextColor: "#999999"
placeholderText: "Enter the log level to download"
color: "black"
// text: root.downloadCid
onTextChanged: root.logLevel = text
anchors.top: downloadManifestsButton.bottom
anchors.topMargin: 50
anchors.horizontalCenter: parent.horizontalCenter
}
Button {
id: logLevelButton
objectName: "logLevelButton"
text: "Log level"
onClicked: root.backend.updateLogLevel(root.logLevel)
anchors.top: logLevelField.bottom
anchors.horizontalCenter: parent.horizontalCenter
enabled: root.isRunning
anchors.topMargin: 10
}
// Log level section
// TextField {
// id: logLevelField
// placeholderTextColor: "#999999"
// placeholderText: "Enter the log level to download"
// color: "black"
// // text: root.downloadCid
// onTextChanged: root.logLevel = text
// anchors.top: manifestTableContainer.bottom
// anchors.topMargin: 30
// anchors.horizontalCenter: parent.horizontalCenter
// }
// Button {
// id: logLevelButton
// objectName: "logLevelButton"
// text: "Log level"
// onClicked: root.backend.updateLogLevel(root.logLevel)
// anchors.top: logLevelField.bottom
// anchors.horizontalCenter: parent.horizontalCenter
// enabled: root.isRunning
// anchors.topMargin: 10
// }
// TextEdit {
// id: selectableText
@ -469,6 +776,22 @@ Rectangle {
}
}
FileDialog {
id: manifestSaveDialog
fileMode: FileDialog.SaveFile
onAccepted: {
if (root.pendingDownloadManifest) {
root.backend.tryDownloadFile(
root.pendingDownloadManifest["cid"],
manifestSaveDialog.selectedFile)
root.pendingDownloadManifest = null
}
}
onRejected: {
root.pendingDownloadManifest = null
}
}
Rectangle {
id: debugPanel
anchors.left: parent.left