import QtQuick import QtQuick.Controls import QtQuick.Layouts import Flora ApplicationWindow { id: window width: 1280 height: 800 minimumWidth: 900 minimumHeight: 600 visible: true title: "Flora" color: Theme.bg palette.window: Theme.bg palette.windowText: Theme.text palette.base: Theme.surface palette.text: Theme.text palette.highlight: Theme.accent palette.highlightedText: "white" property string windowVault: "" readonly property string activeVault: { if (fileStore.ready) return fileStore.vault if (windowVault.length > 0) return windowVault return vaultManager.currentVault } WelcomeWizard { id: welcomeWizard firstRun: vaultManager.vaults.length === 0 onFinished: { if (fileStore.ready) { windowController.openWindow(welcomeWizard.vaultPath) } else { fileStore.openVault(welcomeWizard.vaultPath) root_status("Хранилище открыто") } } } function openWizard() { welcomeWizard.firstRun = false welcomeWizard.open() } function root_status(msg, ok) { // статус можно показать в UI, пока просто игнорируем } Component.onCompleted: { if (windowVault.length > 0) { fileStore.openVault(windowVault) } else if (vaultManager.currentVault.length > 0) { fileStore.openVault(vaultManager.currentVault) } if (vaultManager.vaults.length === 0) { welcomeWizard.open() } } menuBar: MenuBar { // Переопределяем цвета под тему (Basic стиль не красит сам) background: Rectangle { color: Theme.bgAlt; border.color: Theme.divider } Menu { title: qsTr("&Файл") Action { text: qsTr("Добавить хранилище…"); onTriggered: window.openWizard() } Menu { title: qsTr("Экспорт") Action { text: qsTr("В HTML…"); enabled: false } Action { text: qsTr("В PDF…"); enabled: false } } MenuSeparator { } Action { text: qsTr("Выход"); onTriggered: window.close() } } Menu { title: qsTr("&Правка") Action { text: qsTr("Отменить"); shortcut: "Ctrl+Z"; enabled: false } Action { text: qsTr("Повторить"); shortcut: "Ctrl+Shift+Z"; enabled: false } MenuSeparator { } Action { text: qsTr("История файла…") shortcut: "Ctrl+H" enabled: documentManager.activeIndex >= 0 && fileStore.ready onTriggered: { const p = documentManager.pathAt(documentManager.activeIndex) if (p.length>0) { historyDialog.vaultPath = fileStore.vault historyDialog.filePath = p historyDialog.open() } } } Action { text: qsTr("Вернуться к предыдущей версии…") enabled: documentManager.activeIndex >= 0 && fileStore.ready onTriggered: { const p = documentManager.pathAt(documentManager.activeIndex) if (p.length>0) { historyDialog.vaultPath = fileStore.vault historyDialog.filePath = p historyDialog.open() } } } } Menu { title: qsTr("&Вид") ActionGroup { id: themeGroup } Action { text: qsTr("Светлая тема") checkable: true checked: settingsStore.theme !== "dark" ActionGroup.group: themeGroup onTriggered: settingsStore.theme = "light" } Action { text: qsTr("Тёмная тема") checkable: true checked: settingsStore.theme === "dark" ActionGroup.group: themeGroup onTriggered: settingsStore.theme = "dark" } MenuSeparator { } Action { text: qsTr("Сбросить ширины панелей") onTriggered: { settingsStore.leftPanelWidth = 220 settingsStore.rightPanelWidth = 220 leftPanel.width = 220 rightPanel.width = 220 } } } Menu { title: qsTr("&Справка") Action { text: qsTr("О программе") onTriggered: aboutDialog.open() } } } Dialog { id: aboutDialog title: qsTr("О программе") standardButtons: Dialog.Ok modal: true anchors.centerIn: parent background: Rectangle { color: Theme.surface; border.color: Theme.border; radius: 8 } Label { color: Theme.text text: "Flora\nМинималистичный markdown-заметочник (GPL-3.0)\n" + "Версия " + Qt.application.version } } HistoryDialog { id: historyDialog onRestored: { // после восстановления обновляем граф и статус if (fileStore.ready) vaultIndex.rebuild(fileStore.vault) } } Popup { id: vaultSwitcher x: 12 y: window.height - height - 12 width: 340 modal: false closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside background: Rectangle { color: Theme.surface; border.color: Theme.borderStrong; radius: 8 } ColumnLayout { width: parent.width spacing: 8 Label { text: qsTr("Хранилища") font.bold: true color: Theme.text } ListView { id: vaultList Layout.preferredHeight: Math.min(200, contentHeight) Layout.fillWidth: true model: vaultManager.vaults clip: true ScrollBar.vertical: ScrollBar {} delegate: ItemDelegate { width: ListView.view.width text: modelData highlighted: modelData === window.activeVault palette.highlight: Theme.accent onClicked: { if (modelData === window.activeVault) { vaultSwitcher.close() } else { vaultManager.selectVault(modelData) windowController.openWindow(modelData) vaultSwitcher.close() } } } } Label { visible: vaultList.count === 0 text: qsTr("Хранилищ пока нет") color: Theme.textMuted } Label { text: qsTr("Текущее: %1").arg(window.activeVault.length > 0 ? window.activeVault : "—") color: Theme.textSecondary elide: Text.ElideMiddle Layout.fillWidth: true font.pixelSize: 11 } Button { Layout.fillWidth: true text: qsTr("Добавить новое хранилище…") onClicked: { vaultSwitcher.close(); window.openWizard() } } } } SplitView { id: mainSplit anchors.fill: parent orientation: Qt.Horizontal // Увеличенная зона захвата — работает по всей высоте handle: Rectangle { implicitWidth: 12 color: "transparent" Rectangle { anchors.centerIn: parent width: 1; height: parent.height color: Theme.divider } Rectangle { anchors.fill: parent color: "transparent" MouseArea { anchors.fill: parent cursorShape: Qt.SplitHCursor hoverEnabled: true onPressed: mouse.accepted = false } } } FileTreePanel { id: leftPanel SplitView.preferredWidth: settingsStore.leftPanelWidth SplitView.minimumWidth: 180 SplitView.maximumWidth: 500 onWidthChanged: { if (width >= 180 && width <= 500) settingsStore.leftPanelWidth = width } onOpenVaultRequested: vaultSwitcher.open() onOpenFileRequested: (path) => { if (documentManager.isTextForEditor(path)) { documentManager.openDocument(path) } else { leftPanel.showStatus(qsTr("Предпросмотр HTML/PDF/изображений появится в Фазе 6-10"), false) } } onHistoryRequested: (path) => { historyDialog.vaultPath = fileStore.vault historyDialog.filePath = path historyDialog.open() } } EditorPanel { id: centerPanel SplitView.fillWidth: true SplitView.minimumWidth: 300 height: window.height - (menuBar ? menuBar.height : 0) onHistoryRequested: (path) => { historyDialog.vaultPath = fileStore.vault historyDialog.filePath = path historyDialog.open() } } RightPanel { id: rightPanel SplitView.preferredWidth: settingsStore.rightPanelWidth SplitView.minimumWidth: 200 SplitView.maximumWidth: 500 onWidthChanged: { if (width >= 200 && width <= 500) settingsStore.rightPanelWidth = width } } } }