Files
Flora/qml/wizard/WelcomeWizard.qml
T
grendervill 95c03040c7 Prod: полный аудит и стабилизация — исправлены критические баги, расширено покрытие тестами, подготовлен бандл
- FileStore: защита от выхода за пределы хранилища (isInsideVault), валидация имён (запрет скрытых, зарезервированных, управляющих символов), проверка destDir, лимит watcher, dailyNote валидация, openVault проверка isDir
- DocumentManager: лимит 10МБ, кламп автосейва 0.5-120с, защита от OOM, бинарный детект, проверка путей
- VaultIndex: исправлен парсинг тегов (заголовки, инлайн-код), case-insensitive поиск, дедупликация рёбер, защита от traversal, лимит глубины, проверка vaultExists
- MdParser: блокировка всех on* событий, javascript:/data:, esc кавычек, MIME fallback, normalizeLang расширен (c/cpp/py/js и кириллица), wiki глобальный поиск, image лимит 3МБ, защита clipboard
- GitManager: commitAll с GIT_INDEX_ADD_DEFAULT и поддержкой пустых коммитов/удалений, fileGitStatus с cleanPath и проверкой префикса, repoExists/hasCommits с cleanPath, initRepo валидация
- SettingsStore: encodeKey/encodeRel для QSettings Ini, flat хранение viewMode, sync после записи, кламп ширин панелей, миграция старых ключей
- SyncManager: счётчик busy, обработка httpStatus при network error, валидация токена/провайдера/URL, GitHub Enterprise URL, таймаут
- VaultManager: валидация путей (корень, файл vs папка, права), проверка вложенности
- WindowController: per-window FileStore/DocumentManager/VaultIndex, cleanPath, закрытие окна через QQuickWindow::closing, обработка ошибок компонента
- QML: TableEditor пустая строка, WelcomeWizard QUrl парсинг, FileTree фильтр, GraphView лимит 600 узлов, RightPanel debounce rebuild, Theme singleton, MonthCalendar и др.
- Тесты: добавлены flora_settings_test, flora_sync_test, flora_extended_test, flora_integration_test — покрытие всех модулей, 20/20 зелёных
- Сборка: обновлён Flora.app (4.6М бинарь -> 173М бандл, ad-hoc подпись, проверен offscreen запуск, версия 0.0.1 + cmark 0.29 + libgit2 1.9)
- .gitignore: добавлены Testing/, coverage, gcda/gcov, __pycache__, .tmp, test-vault и др.
2026-09-01 19:25:07 +03:00

196 lines
8.2 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Dialogs
import Flora
Dialog {
id: root
required property bool firstRun
readonly property int stepFolder: 0
readonly property int stepSetup: 1
property int step: stepFolder
property string vaultPath: ""
property string setupStatus: ""
signal finished()
title: firstRun ? qsTr("Первое знакомство: создайте хранилище") : qsTr("Новое хранилище")
modal: true
width: 560
height: 480
anchors.centerIn: parent
closePolicy: Dialog.NoAutoClose
background: Rectangle { color: Theme.surface; border.color: Theme.borderStrong; radius: 10 }
// индикатор шагов
header: Rectangle {
height: 48
color: Theme.bgAlt
radius: 10
RowLayout {
anchors.fill: parent
anchors.margins: 12
spacing: 8
Repeater {
model: 2
Rectangle {
Layout.fillWidth: true
height: 4
radius: 2
color: index <= root.step ? Theme.accent : Theme.border
}
}
}
Rectangle { anchors.bottom: parent.bottom; anchors.left: parent.left; anchors.right: parent.right; height: 1; color: Theme.divider }
}
StackLayout {
id: stack
anchors.fill: parent
currentIndex: root.step
ColumnLayout {
spacing: 12
Label {
text: qsTr("Выберите локальную папку — это будет ваше хранилище.\nМожно указать новую папку (будет создана) или уже существующую с файлами.")
wrapMode: Text.Wrap
Layout.fillWidth: true
color: Theme.textSecondary
}
RowLayout {
Layout.fillWidth: true
TextField {
id: pathField
Layout.fillWidth: true
placeholderText: qsTr("Путь к папке…")
placeholderTextColor: Theme.textFaint
text: root.vaultPath
color: Theme.text
background: Rectangle { color: Theme.bgAlt; border.color: pathField.activeFocus ? Theme.accent : Theme.border; radius: 6 }
onTextChanged: root.vaultPath = text
}
Button { text: qsTr("Обзор…"); onClicked: folderDialog.open() }
}
FolderDialog {
id: folderDialog
title: qsTr("Выберите папку хранилища")
onAccepted: {
// Используем toLocalFile где возможно для корректной обработки unicode и пробелов
let p = ""
try {
const url = selectedFolder
if (url && url.toString) {
// QUrl -> local file
// В QML FolderDialog selectedFolder уже QUrl, пробуем toString и декодируем
const s = url.toString()
if (s.startsWith("file://")) {
// Пытаемся через decodeURIComponent и обрезку file://
p = decodeURIComponent(s)
if (p.startsWith("file://")) p = p.substring(7)
// Windows: /C:/path -> C:/path
if (p.length >= 3 && p[0] === "/" && p[2] === ":" && /[A-Za-z]/.test(p[1])) p = p.substring(1)
// Убираем якорь/параметры если есть
const hash = p.indexOf('#')
if (hash >= 0) p = p.substring(0, hash)
const q = p.indexOf('?')
if (q >= 0) p = p.substring(0, q)
} else {
p = decodeURIComponent(s)
}
}
} catch(e) { console.warn("FolderDialog parse error", e) }
if (p.length === 0 && selectedFolder) {
try { p = selectedFolder.toString() } catch(e) {}
}
root.vaultPath = p
}
}
Rectangle {
Layout.fillWidth: true
visible: step1Error.text.length > 0
color: Theme.errorBg
border.color: Theme.errorText
radius: 6
height: step1Error.implicitHeight + 12
Label {
id: step1Error
anchors.fill: parent
anchors.margins: 6
color: Theme.errorText
wrapMode: Text.Wrap
text: ""
}
}
Item { Layout.fillHeight: true }
Label { text: qsTr("Шаг 1 из 2 — выбор папки"); color: Theme.textFaint; font.pixelSize: 11 }
RowLayout {
Layout.fillWidth: true
Item { Layout.fillWidth: true }
Button { text: qsTr("Отмена"); onClicked: root.reject() }
Button {
text: qsTr("Далее")
highlighted: true
enabled: root.vaultPath.length > 0 && !busyOverlay.running
onClicked: {
step1Error.text = ""
busyOverlay.running = true
const err = vaultManager.createVault(root.vaultPath)
busyOverlay.running = false
if (err.length > 0) { step1Error.text = err; return }
root.step = stepSetup
}
}
}
}
ColumnLayout {
spacing: 12
Label {
text: qsTr("Хранилище готово. Внутри автоматически создан git-репозиторий.\nПри желании подключите синхронизацию:")
wrapMode: Text.Wrap
Layout.fillWidth: true
color: Theme.textSecondary
}
GridLayout {
columns: 2
Layout.fillWidth: true
Layout.fillHeight: true
columnSpacing: 8
rowSpacing: 8
Button { text: "GitHub"; Layout.fillWidth: true; onClicked: openProviderGuide(0, "GitHub") }
Button { text: "GitLab"; Layout.fillWidth: true; onClicked: openProviderGuide(1, "GitLab") }
Button { text: "Gitea"; Layout.fillWidth: true; onClicked: openProviderGuide(2, "Gitea") }
Button { text: "Nextcloud (WebDAV)"; Layout.fillWidth: true; onClicked: openProviderGuide(3, "Nextcloud (WebDAV)") }
Button { text: "Syncthing"; Layout.fillWidth: true; Layout.columnSpan: 2; onClicked: openProviderGuide(4, "Syncthing") }
function openProviderGuide(idx, name) {
providerGuideForButtons.providerIndex = idx
providerGuideForButtons.providerName = name
providerGuideForButtons.connected = false
providerGuideForButtons.open()
}
}
RowLayout {
Layout.fillWidth: true
Button { text: qsTr("Назад"); onClicked: root.step = stepFolder }
Item { Layout.fillWidth: true }
Button {
text: qsTr("Готово")
highlighted: true
onClicked: { vaultManager.selectVault(root.vaultPath); root.accept(); root.finished() }
}
}
}
}
BusyIndicator { id: busyOverlay; anchors.centerIn: parent; running: false; visible: running }
ProviderSetupGuide { id: providerGuideForButtons }
onRejected: {
if (firstRun) visible = false
}
}