flea-qml 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +478 -2
- package/THIRD-PARTY-NOTICES.md +29 -0
- package/demos/App.java +5 -0
- package/demos/Dockerfile +4 -0
- package/demos/Main.kt +3 -0
- package/demos/Program.cs +1 -0
- package/demos/README.md +12 -0
- package/demos/app.dart +3 -0
- package/demos/app.deb +0 -0
- package/demos/app.js +1 -0
- package/demos/backup.tar +0 -0
- package/demos/budget.xlsx +0 -0
- package/demos/build.sh +2 -0
- package/demos/bundle.zip +0 -0
- package/demos/clip.mp4 +0 -0
- package/demos/data.sqlite +0 -0
- package/demos/disk.iso +0 -0
- package/demos/engine.cpp +5 -0
- package/demos/firmware.bin +0 -0
- package/demos/flake.nix +4 -0
- package/demos/folder/inside.txt +1 -0
- package/demos/font.ttf +0 -0
- package/demos/index.html +5 -0
- package/demos/index.php +2 -0
- package/demos/init.lua +1 -0
- package/demos/letter.docx +0 -0
- package/demos/lib.rs +3 -0
- package/demos/main.c +6 -0
- package/demos/main.h +3 -0
- package/demos/math-and-diagrams.md +200 -0
- package/demos/notes.tar.gz +0 -0
- package/demos/notes.txt +3 -0
- package/demos/photo.png +0 -0
- package/demos/program +2 -0
- package/demos/report.pdf +32 -0
- package/demos/script.py +1 -0
- package/demos/server.go +7 -0
- package/demos/settings.json +4 -0
- package/demos/slides.pptx +0 -0
- package/demos/song.mp3 +0 -0
- package/demos/style.css +4 -0
- package/demos/tool.rb +1 -0
- package/demos/types.ts +2 -0
- package/docs/pele.md +23 -0
- package/docs/protocol.md +3420 -0
- package/docs/temml.md +63 -0
- package/index.js +107 -0
- package/package.json +34 -4
- package/src/archive.js +402 -0
- package/src/archiveops.js +697 -0
- package/src/backend.js +369 -0
- package/src/bulkrename.js +95 -0
- package/src/clip-host.js +76 -0
- package/src/clip.js +462 -0
- package/src/code.js +168 -0
- package/src/compare.js +74 -0
- package/src/create.js +128 -0
- package/src/dirsize.js +147 -0
- package/src/figure-svg.js +530 -0
- package/src/figure-thread.js +23 -0
- package/src/figure.js +132 -0
- package/src/find.js +55 -0
- package/src/flea.js +39 -0
- package/src/fsinfo.js +39 -0
- package/src/highlight.js +141 -0
- package/src/html.js +52 -0
- package/src/kinds.js +89 -0
- package/src/listing.js +130 -0
- package/src/markdown-html.js +317 -0
- package/src/mathml.js +186 -0
- package/src/menu.js +180 -0
- package/src/meta.js +129 -0
- package/src/office.js +810 -0
- package/src/order.js +100 -0
- package/src/peek.js +49 -0
- package/src/pele-wrapper.js +695 -0
- package/src/permissions.js +180 -0
- package/src/places.js +313 -0
- package/src/preview-language.js +10 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +326 -0
- package/src/uistate.js +393 -0
- package/src/uistore.js +168 -0
- package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
- package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
- package/src/vendor/LICENSES/MIT-katex.txt +21 -0
- package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
- package/src/vendor/LICENSES/MIT-pele.txt +40 -0
- package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
- package/src/vendor/LICENSES/MIT-temml.txt +21 -0
- package/src/vendor/pele.js +145 -0
- package/src/vendor/temml.mjs +14708 -0
- package/src/watch.js +98 -0
- package/src/wayland/client.js +109 -0
- package/src/wayland/protocol.js +55 -0
- package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
- package/src/wayland/protocols/wayland.xml +3248 -0
- package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
- package/src/wayland/resource-io.js +6 -0
- package/src/wayland/socket-discovery.js +26 -0
- package/src/wayland/transport-posix.js +120 -0
- package/src/wayland/transport-win32.js +11 -0
- package/src/wayland/transport.js +3 -0
- package/src/wayland/win32-resources.js +80 -0
- package/src/wayland/win32-wire.js +62 -0
- package/test/archive.test.js +80 -0
- package/test/bench-markdown-scroll.mjs +190 -0
- package/test/clip-host.test.js +113 -0
- package/test/code-preview.test.js +35 -0
- package/test/compare-ui-paths.test.js +24 -0
- package/test/compare.test.js +54 -0
- package/test/delete-trash.test.js +67 -0
- package/test/dirsize.test.js +138 -0
- package/test/file-ops.test.js +241 -0
- package/test/find-contents.test.js +161 -0
- package/test/find-input.test.js +66 -0
- package/test/fixtures/pele-parity.json +1006 -0
- package/test/highlight-launcher.test.js +35 -0
- package/test/html-preview.test.js +22 -0
- package/test/input.test.js +15 -0
- package/test/keymap.test.js +26 -0
- package/test/language-icons.test.js +36 -0
- package/test/launcher.test.js +51 -0
- package/test/manual-code-scroll.mjs +28 -0
- package/test/manual-column-scroll.mjs +24 -0
- package/test/manual-compare-ui.mjs +20 -0
- package/test/manual-find-status.mjs +37 -0
- package/test/manual-html-security.mjs +23 -0
- package/test/manual-html-ui.mjs +30 -0
- package/test/manual-syntax-preview.mjs +26 -0
- package/test/manual-trash-ui.mjs +86 -0
- package/test/markdown-diagrams.test.js +93 -0
- package/test/markdown-html.test.js +105 -0
- package/test/mathml.test.js +92 -0
- package/test/menu-transfer.test.js +57 -0
- package/test/office-preview.test.js +154 -0
- package/test/pele-parity.test.js +28 -0
- package/test/places.test.js +134 -0
- package/test/preview-keys.test.js +60 -0
- package/test/preview-language.test.js +22 -0
- package/test/status-filename.test.js +20 -0
- package/test/transfer-replace-merge.test.js +187 -0
- package/test/trash-address.test.js +23 -0
- package/test/trash-view.test.js +37 -0
- package/test/trashbrowse.test.js +135 -0
- package/ui/AboutFacts.qml +78 -0
- package/ui/ArchiveCards.qml +68 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +771 -0
- package/ui/BulkOldNames.qml +47 -0
- package/ui/BulkRenameDialog.qml +380 -0
- package/ui/CardFrame.qml +67 -0
- package/ui/CardScroll.qml +79 -0
- package/ui/CheckBox.qml +73 -0
- package/ui/ChromeBar.qml +360 -0
- package/ui/ChromeButton.qml +131 -0
- package/ui/ChromeModes.qml +43 -0
- package/ui/CodeLines.qml +256 -0
- package/ui/CodeRefusal.qml +52 -0
- package/ui/CollideConfirm.qml +147 -0
- package/ui/CollideHost.qml +167 -0
- package/ui/ColumnPane.qml +523 -0
- package/ui/ColumnRow.qml +380 -0
- package/ui/ColumnsArea.qml +594 -0
- package/ui/ColumnsLanding.qml +147 -0
- package/ui/CompareView.qml +321 -0
- package/ui/ContextMenu.qml +743 -0
- package/ui/ConvertDialog.qml +456 -0
- package/ui/CountedWorker.qml +7 -0
- package/ui/Crumb.qml +64 -0
- package/ui/CrumbDrop.qml +25 -0
- package/ui/CsvTable.qml +394 -0
- package/ui/CursorMark.qml +41 -0
- package/ui/DefaultClaim.qml +130 -0
- package/ui/DeviceLock.qml +47 -0
- package/ui/DeviceMounts.qml +540 -0
- package/ui/DialogButton.qml +150 -0
- package/ui/DialogField.qml +147 -0
- package/ui/DialogHeader.qml +31 -0
- package/ui/DialogScrim.qml +20 -0
- package/ui/DialogTitle.qml +37 -0
- package/ui/Divider.qml +9 -0
- package/ui/DropInto.qml +126 -0
- package/ui/DropTarget.qml +35 -0
- package/ui/DropboxMark.qml +60 -0
- package/ui/EmptyState.qml +150 -0
- package/ui/EscapeHint.qml +11 -0
- package/ui/FactsTable.qml +54 -0
- package/ui/FastScrollHandler.qml +476 -0
- package/ui/FatNamesConfirm.qml +157 -0
- package/ui/Favourites.qml +143 -0
- package/ui/FigureService.qml +120 -0
- package/ui/FileDrag.qml +267 -0
- package/ui/FilterStrip.qml +133 -0
- package/ui/FitMetrics.qml +22 -0
- package/ui/FlashDriveRows.qml +137 -0
- package/ui/FlashHost.qml +72 -0
- package/ui/FlashImageRow.qml +104 -0
- package/ui/FlashRunner.qml +119 -0
- package/ui/FlashStepRow.qml +172 -0
- package/ui/FlashStrip.qml +38 -0
- package/ui/FlashView.qml +397 -0
- package/ui/FleaMark.qml +88 -0
- package/ui/FlyoutFade.qml +23 -0
- package/ui/FontPane.qml +170 -0
- package/ui/Glass.qml +114 -0
- package/ui/GlassTagger.qml +59 -0
- package/ui/Glyph.qml +64 -0
- package/ui/GlyphSolid.qml +22 -0
- package/ui/GridArea.qml +664 -0
- package/ui/GridTile.qml +325 -0
- package/ui/GvfsBridge.qml +164 -0
- package/ui/Header.qml +433 -0
- package/ui/HoverTimers.qml +39 -0
- package/ui/HtmlClip.qml +30 -0
- package/ui/HtmlPane.qml +63 -0
- package/ui/HyprlandMark.qml +45 -0
- package/ui/Ipc.qml +1005 -0
- package/ui/IpcControls.qml +7 -0
- package/ui/IpcMenuState.qml +48 -0
- package/ui/IpcPreviewState.qml +185 -0
- package/ui/JumpPath.qml +51 -0
- package/ui/KeymapSheet.qml +561 -0
- package/ui/KeymapSheetResult.qml +89 -0
- package/ui/KindStack.qml +40 -0
- package/ui/LangMark.qml +51 -0
- package/ui/LangMarkLoader.qml +26 -0
- package/ui/List.qml +480 -0
- package/ui/LoadingState.qml +54 -0
- package/ui/LocalSend.qml +56 -0
- package/ui/LocalSendMark.qml +73 -0
- package/ui/MarkdownBlockView.qml +311 -0
- package/ui/MarkdownBlocks.qml +35 -0
- package/ui/MarkdownFigure.qml +254 -0
- package/ui/MarkdownImages.qml +72 -0
- package/ui/MarkdownList.qml +81 -0
- package/ui/MarkdownMathsText.qml +97 -0
- package/ui/MarkdownPane.qml +83 -0
- package/ui/MarkdownPictures.qml +41 -0
- package/ui/MarkdownQuote.qml +52 -0
- package/ui/MarkdownRemote.qml +82 -0
- package/ui/MarkdownSource.qml +155 -0
- package/ui/MarkdownTable.qml +222 -0
- package/ui/MarkdownTableScroll.qml +64 -0
- package/ui/MarkdownTableWheel.qml +126 -0
- package/ui/MarkdownText.qml +87 -0
- package/ui/MarkdownWorker.js +132 -0
- package/ui/MatchText.qml +99 -0
- package/ui/MediaSession.qml +38 -0
- package/ui/MediaSound.qml +26 -0
- package/ui/MediaStrip.qml +191 -0
- package/ui/MediaTrack.qml +36 -0
- package/ui/MediaVolume.qml +54 -0
- package/ui/MenuActionDialog.qml +396 -0
- package/ui/MenuEdgeFade.qml +24 -0
- package/ui/MenuHost.qml +227 -0
- package/ui/MenuRow.qml +351 -0
- package/ui/MountListing.qml +100 -0
- package/ui/NetworkDialog.qml +435 -0
- package/ui/NetworkForm.qml +426 -0
- package/ui/NetworkMounts.qml +913 -0
- package/ui/NetworkPlaces.qml +127 -0
- package/ui/OpenWithDialog.qml +613 -0
- package/ui/Opener.qml +115 -0
- package/ui/Pane.qml +1296 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +239 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +605 -0
- package/ui/ParkedWait.qml +9 -0
- package/ui/PathEntry.qml +186 -0
- package/ui/PathJump.qml +400 -0
- package/ui/PdfViewer.qml +297 -0
- package/ui/PermissionsDialog.qml +658 -0
- package/ui/PermissionsHeadings.qml +31 -0
- package/ui/PhoneMounts.qml +67 -0
- package/ui/PickerAnswers.qml +75 -0
- package/ui/PickerBox.qml +26 -0
- package/ui/PickerChrome.qml +295 -0
- package/ui/PickerFilter.qml +97 -0
- package/ui/PickerGrid.qml +308 -0
- package/ui/PickerHeader.qml +28 -0
- package/ui/PickerLifecycle.qml +27 -0
- package/ui/PickerList.qml +218 -0
- package/ui/PickerListing.qml +149 -0
- package/ui/PickerPathBar.qml +127 -0
- package/ui/PickerPlaces.qml +189 -0
- package/ui/PickerRecent.qml +92 -0
- package/ui/PickerSave.qml +101 -0
- package/ui/PickerSelection.qml +155 -0
- package/ui/PickerWindow.qml +846 -0
- package/ui/PowerSectorsReader.qml +10 -0
- package/ui/PrebuildDriver.qml +146 -0
- package/ui/Preview.qml +1004 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +66 -0
- package/ui/PreviewColumn.qml +551 -0
- package/ui/PreviewEdge.qml +63 -0
- package/ui/PreviewFacts.qml +30 -0
- package/ui/PreviewFactsLine.qml +34 -0
- package/ui/PreviewHtml.qml +233 -0
- package/ui/PreviewImage.qml +253 -0
- package/ui/PreviewImagePan.qml +13 -0
- package/ui/PreviewImageSharp.qml +32 -0
- package/ui/PreviewLines.qml +106 -0
- package/ui/PreviewMarkdown.qml +706 -0
- package/ui/PreviewMedia.qml +250 -0
- package/ui/PreviewPdf.qml +482 -0
- package/ui/PreviewScroll.qml +201 -0
- package/ui/PreviewSwap.qml +335 -0
- package/ui/PreviewText.qml +149 -0
- package/ui/ProtocolChip.qml +18 -0
- package/ui/QuickLookArchive.qml +38 -0
- package/ui/QuickLookBar.qml +103 -0
- package/ui/QuickLookPrepare.qml +355 -0
- package/ui/QuickLookSwap.qml +10 -0
- package/ui/QuickLookWarm.qml +65 -0
- package/ui/RailDrop.qml +155 -0
- package/ui/RailHeading.qml +19 -0
- package/ui/RailPlaces.qml +38 -0
- package/ui/RailTrashRow.qml +67 -0
- package/ui/RenameField.qml +353 -0
- package/ui/RendererRetry.qml +11 -0
- package/ui/ResizeHandle.qml +52 -0
- package/ui/Reveal.qml +54 -0
- package/ui/Row.qml +476 -0
- package/ui/RowDrag.qml +213 -0
- package/ui/RowDropTarget.qml +27 -0
- package/ui/RunCard.qml +286 -0
- package/ui/Scripts.qml +65 -0
- package/ui/SearchStrip.qml +232 -0
- package/ui/SelectionBand.qml +140 -0
- package/ui/SelectionPreview.qml +370 -0
- package/ui/SettingsFavourite.qml +122 -0
- package/ui/SettingsGroup.qml +96 -0
- package/ui/SettingsHint.qml +12 -0
- package/ui/SettingsMetrics.qml +29 -0
- package/ui/SettingsPane.qml +178 -0
- package/ui/SettingsPanel.qml +547 -0
- package/ui/SettingsRail.qml +128 -0
- package/ui/SettingsRow.qml +409 -0
- package/ui/SettingsRuler.qml +85 -0
- package/ui/SettingsSegment.qml +85 -0
- package/ui/ShareBrowser.qml +113 -0
- package/ui/ShareLink.qml +67 -0
- package/ui/ShelfPins.qml +101 -0
- package/ui/Sidebar.qml +732 -0
- package/ui/SidebarRow.qml +358 -0
- package/ui/Spinner.qml +60 -0
- package/ui/StableModel.qml +89 -0
- package/ui/StateMessage.qml +84 -0
- package/ui/StatusBar.qml +512 -0
- package/ui/StatusFileName.qml +27 -0
- package/ui/TabBar.qml +673 -0
- package/ui/TabDragGeometry.qml +60 -0
- package/ui/TagMenuMark.qml +52 -0
- package/ui/TagRailSection.qml +53 -0
- package/ui/TagSlot.qml +40 -0
- package/ui/TagState.qml +150 -0
- package/ui/TagSwatch.qml +28 -0
- package/ui/Taildrop.qml +86 -0
- package/ui/TailscaleMark.qml +50 -0
- package/ui/Theme.qml +460 -0
- package/ui/ThemeColors.qml +43 -0
- package/ui/TransferCard.qml +239 -0
- package/ui/TrashConfirm.qml +129 -0
- package/ui/TrashDropHandler.qml +11 -0
- package/ui/TrashHost.qml +202 -0
- package/ui/TrashMonitor.qml +30 -0
- package/ui/TrashToolbar.qml +19 -0
- package/ui/TrashView.qml +492 -0
- package/ui/UnlockDialog.qml +450 -0
- package/ui/UnlockHost.qml +206 -0
- package/ui/UpdateCheck.qml +97 -0
- package/ui/UserDirs.qml +25 -0
- package/ui/ViewState.qml +565 -0
- package/ui/ViewportScrollBar.qml +195 -0
- package/ui/ViewportScrollBars.qml +25 -0
- package/ui/WebPreview.qml +33 -0
- package/ui/WebPreviewView.qml +26 -0
- package/ui/WindowBody.qml +827 -0
- package/ui/WindowDrag.qml +35 -0
- package/ui/boot/Commons/AudioNodes.qml +50 -0
- package/ui/boot/Commons/AudioNodesModel.js +96 -0
- package/ui/boot/Commons/Border.qml +243 -0
- package/ui/boot/Commons/BorderGeometry.js +373 -0
- package/ui/boot/Commons/Color.qml +258 -0
- package/ui/boot/Commons/I18n.qml +118 -0
- package/ui/boot/Commons/InputDevices.qml +112 -0
- package/ui/boot/Commons/InputMethodState.qml +38 -0
- package/ui/boot/Commons/IpcRegistry.qml +77 -0
- package/ui/boot/Commons/Numbers.js +19 -0
- package/ui/boot/Commons/Plural.js +91 -0
- package/ui/boot/Commons/ShellIpc.qml +12 -0
- package/ui/boot/Commons/Style.qml +561 -0
- package/ui/boot/Commons/Translations.qml +100 -0
- package/ui/boot/Commons/UntypedInput.qml +152 -0
- package/ui/boot/Commons/Util.qml +188 -0
- package/ui/boot/Commons/qmldir +13 -0
- package/ui/boot/LICENSE-omarchy +20 -0
- package/ui/boot/Ui/BackgroundMedia.qml +49 -0
- package/ui/boot/Ui/BarIconButton.qml +106 -0
- package/ui/boot/Ui/BarIconContent.qml +38 -0
- package/ui/boot/Ui/BarIndicator.qml +53 -0
- package/ui/boot/Ui/BarWidget.qml +50 -0
- package/ui/boot/Ui/BorderOverlay.qml +54 -0
- package/ui/boot/Ui/BorderSurface.qml +40 -0
- package/ui/boot/Ui/Button.qml +224 -0
- package/ui/boot/Ui/ButtonGroup.qml +131 -0
- package/ui/boot/Ui/ConfirmDialog.qml +134 -0
- package/ui/boot/Ui/CursorSurface.qml +42 -0
- package/ui/boot/Ui/Dropdown.qml +245 -0
- package/ui/boot/Ui/FilterInput.qml +26 -0
- package/ui/boot/Ui/IconContentBounds.qml +141 -0
- package/ui/boot/Ui/KeyboardPanel.qml +453 -0
- package/ui/boot/Ui/MultiSelect.qml +624 -0
- package/ui/boot/Ui/NumberField.qml +86 -0
- package/ui/boot/Ui/OpticalGlyph.qml +68 -0
- package/ui/boot/Ui/OverlayWindow.qml +102 -0
- package/ui/boot/Ui/Panel.qml +60 -0
- package/ui/boot/Ui/PanelActionButton.qml +105 -0
- package/ui/boot/Ui/PanelController.qml +16 -0
- package/ui/boot/Ui/PanelHero.qml +112 -0
- package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
- package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
- package/ui/boot/Ui/PanelSeparator.qml +19 -0
- package/ui/boot/Ui/PanelSlider.qml +156 -0
- package/ui/boot/Ui/PanelToolTip.qml +50 -0
- package/ui/boot/Ui/PluginBarApi.qml +89 -0
- package/ui/boot/Ui/PointerMoveGate.qml +54 -0
- package/ui/boot/Ui/PopupCard.qml +181 -0
- package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
- package/ui/boot/Ui/SearchableDropdown.qml +354 -0
- package/ui/boot/Ui/SizedImage.qml +125 -0
- package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
- package/ui/boot/Ui/TextField.qml +58 -0
- package/ui/boot/Ui/Toggle.qml +118 -0
- package/ui/boot/Ui/ToggleSwitch.qml +115 -0
- package/ui/boot/Ui/WidgetButton.qml +172 -0
- package/ui/boot/Ui/qmldir +41 -0
- package/ui/boot/fleatab.qml +50 -0
- package/ui/boot/picker.qml +39 -0
- package/ui/boot/shell.qml +122 -0
- package/ui/boot/tabtearoff.qml +51 -0
- package/ui/js/Anchor.js +379 -0
- package/ui/js/AnchorHold.js +162 -0
- package/ui/js/Archive.js +157 -0
- package/ui/js/ArchiveCard.js +44 -0
- package/ui/js/ArchiveOps.js +44 -0
- package/ui/js/BackReturn.js +118 -0
- package/ui/js/BulkRename.js +100 -0
- package/ui/js/Buttons.js +54 -0
- package/ui/js/ChromeKeys.js +116 -0
- package/ui/js/ClipMarks.js +73 -0
- package/ui/js/ClipSelection.js +67 -0
- package/ui/js/Clipboard.js +179 -0
- package/ui/js/Cloud.js +97 -0
- package/ui/js/CodeRuns.js +72 -0
- package/ui/js/Collide.js +212 -0
- package/ui/js/ColumnFit.js +28 -0
- package/ui/js/ColumnMenu.js +111 -0
- package/ui/js/Columns.js +298 -0
- package/ui/js/ColumnsCarry.js +103 -0
- package/ui/js/Compare.js +74 -0
- package/ui/js/Contrast.js +157 -0
- package/ui/js/ControlReader.js +42 -0
- package/ui/js/Convert.js +52 -0
- package/ui/js/CopyAs.js +52 -0
- package/ui/js/Crumbs.js +178 -0
- package/ui/js/Csv.js +231 -0
- package/ui/js/Density.js +44 -0
- package/ui/js/Devices.js +440 -0
- package/ui/js/DirSizes.js +180 -0
- package/ui/js/Drag.js +364 -0
- package/ui/js/DragOut.js +166 -0
- package/ui/js/DropDev.js +52 -0
- package/ui/js/Dropbox.js +41 -0
- package/ui/js/Eject.js +217 -0
- package/ui/js/Errors.js +188 -0
- package/ui/js/ExtThumbs.js +115 -0
- package/ui/js/Facts.js +402 -0
- package/ui/js/FigureDate.js +14 -0
- package/ui/js/Filter.js +285 -0
- package/ui/js/Find.js +46 -0
- package/ui/js/Flash.js +184 -0
- package/ui/js/FlashCard.js +79 -0
- package/ui/js/FlashEndings.js +125 -0
- package/ui/js/FlashPane.js +116 -0
- package/ui/js/FleaBin.js +12 -0
- package/ui/js/Focus.js +502 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +305 -0
- package/ui/js/Fuzzy.js +141 -0
- package/ui/js/GlassTag.js +72 -0
- package/ui/js/Grid.js +33 -0
- package/ui/js/GridGeometry.js +90 -0
- package/ui/js/GridNames.js +153 -0
- package/ui/js/GvfsBridge.js +200 -0
- package/ui/js/HostClipboard.js +29 -0
- package/ui/js/Icons.js +218 -0
- package/ui/js/Input.js +50 -0
- package/ui/js/Jump.js +200 -0
- package/ui/js/KeyName.js +64 -0
- package/ui/js/Keymap.js +401 -0
- package/ui/js/Kinds.js +164 -0
- package/ui/js/LICENSES/ISC-lucide.txt +43 -0
- package/ui/js/LangMarkPaths.js +47 -0
- package/ui/js/LangMarks.js +120 -0
- package/ui/js/Lists.js +11 -0
- package/ui/js/LocalSend.js +39 -0
- package/ui/js/LockedMenu.js +63 -0
- package/ui/js/MakeDefault.js +186 -0
- package/ui/js/MarkBox.js +7 -0
- package/ui/js/Markdown.js +119 -0
- package/ui/js/MarkdownLists.js +86 -0
- package/ui/js/MarkdownMaths.js +58 -0
- package/ui/js/MarkdownPictures.js +32 -0
- package/ui/js/MarkdownPrepared.js +139 -0
- package/ui/js/MarkdownTableFit.js +34 -0
- package/ui/js/Marks.js +148 -0
- package/ui/js/Marquee.js +96 -0
- package/ui/js/Match.js +93 -0
- package/ui/js/Material.js +109 -0
- package/ui/js/MdBlocks.js +437 -0
- package/ui/js/MdBreak.js +71 -0
- package/ui/js/MdChunks.js +70 -0
- package/ui/js/MdContainer.js +137 -0
- package/ui/js/MdDocument.js +337 -0
- package/ui/js/MdEmph.js +97 -0
- package/ui/js/MdEntity.js +108 -0
- package/ui/js/MdEntityTable.js +223 -0
- package/ui/js/MdEscape.js +59 -0
- package/ui/js/MdFront.js +37 -0
- package/ui/js/MdHold.js +32 -0
- package/ui/js/MdHtml.js +307 -0
- package/ui/js/MdHtmlBlock.js +290 -0
- package/ui/js/MdHtmlHeading.js +53 -0
- package/ui/js/MdHtmlImage.js +179 -0
- package/ui/js/MdInline.js +154 -0
- package/ui/js/MdItems.js +212 -0
- package/ui/js/MdLeaf.js +309 -0
- package/ui/js/MdLink.js +196 -0
- package/ui/js/MdMath.js +67 -0
- package/ui/js/MdParagraphs.js +138 -0
- package/ui/js/MdRefs.js +232 -0
- package/ui/js/MdResolve.js +226 -0
- package/ui/js/MdRun.js +300 -0
- package/ui/js/MdUrl.js +226 -0
- package/ui/js/Menu.js +514 -0
- package/ui/js/MenuContext.js +85 -0
- package/ui/js/MenuFit.js +13 -0
- package/ui/js/MenuRefresh.js +74 -0
- package/ui/js/MenuWheel.js +35 -0
- package/ui/js/Messages.js +186 -0
- package/ui/js/Motion.js +16 -0
- package/ui/js/Mounts.js +320 -0
- package/ui/js/MouseNav.js +13 -0
- package/ui/js/Nav.js +329 -0
- package/ui/js/NetFs.js +26 -0
- package/ui/js/OpenWith.js +83 -0
- package/ui/js/Ops.js +585 -0
- package/ui/js/Palette.js +33 -0
- package/ui/js/PathBar.js +260 -0
- package/ui/js/Permissions.js +179 -0
- package/ui/js/Phones.js +112 -0
- package/ui/js/Picker.js +353 -0
- package/ui/js/PlaceMenu.js +96 -0
- package/ui/js/Places.js +265 -0
- package/ui/js/PreviewAsk.js +21 -0
- package/ui/js/PreviewKeys.js +155 -0
- package/ui/js/PreviewSettle.js +29 -0
- package/ui/js/PreviewSwap.js +101 -0
- package/ui/js/PropertyFacts.js +144 -0
- package/ui/js/Protocols.js +296 -0
- package/ui/js/QuickLookKinds.js +27 -0
- package/ui/js/QuickLookRect.js +24 -0
- package/ui/js/RailDrop.js +51 -0
- package/ui/js/RailKeys.js +87 -0
- package/ui/js/RailMenu.js +133 -0
- package/ui/js/Recent.js +135 -0
- package/ui/js/RecentMode.js +156 -0
- package/ui/js/Reload.js +60 -0
- package/ui/js/Renderer.js +9 -0
- package/ui/js/RunFile.js +143 -0
- package/ui/js/Scripts.js +28 -0
- package/ui/js/Scroll.js +304 -0
- package/ui/js/ScrollOff.js +69 -0
- package/ui/js/Search.js +478 -0
- package/ui/js/Selection.js +97 -0
- package/ui/js/Settings.js +476 -0
- package/ui/js/SettingsAbout.js +97 -0
- package/ui/js/SettingsShelf.js +52 -0
- package/ui/js/SheetKeys.js +42 -0
- package/ui/js/SheetQuery.js +281 -0
- package/ui/js/ShelfPile.js +27 -0
- package/ui/js/SlowClick.js +53 -0
- package/ui/js/SlowOp.js +6 -0
- package/ui/js/Sort.js +134 -0
- package/ui/js/Startup.js +63 -0
- package/ui/js/StateWords.js +17 -0
- package/ui/js/Status.js +238 -0
- package/ui/js/Swap.js +166 -0
- package/ui/js/TabMove.js +39 -0
- package/ui/js/Tabs.js +736 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +174 -0
- package/ui/js/TextSize.js +107 -0
- package/ui/js/Thumbs.js +158 -0
- package/ui/js/Transfer.js +172 -0
- package/ui/js/Trash.js +81 -0
- package/ui/js/TrashDates.js +47 -0
- package/ui/js/TrashDrop.js +64 -0
- package/ui/js/UiState.js +438 -0
- package/ui/js/Unlock.js +152 -0
- package/ui/js/Update.js +130 -0
- package/ui/js/UserDirs.js +56 -0
- package/ui/qmldir +228 -0
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
import Quickshell
|
|
2
|
+
import QtQuick
|
|
3
|
+
import qs.Commons
|
|
4
|
+
import "."
|
|
5
|
+
import "." as Flea
|
|
6
|
+
import "js/Focus.js" as Focus
|
|
7
|
+
import "js/MarkdownPrepared.js" as Prepared
|
|
8
|
+
import "js/TextSize.js" as TextSize
|
|
9
|
+
import "js/MouseNav.js" as MouseNav
|
|
10
|
+
import "js/Nav.js" as Nav
|
|
11
|
+
import "js/Ops.js" as Ops
|
|
12
|
+
import "js/RailKeys.js" as RailKeys
|
|
13
|
+
import "js/RailMenu.js" as RailMenu
|
|
14
|
+
import "js/Search.js" as Search
|
|
15
|
+
import "js/Startup.js" as Startup
|
|
16
|
+
import "js/Tabs.js" as Tabs
|
|
17
|
+
|
|
18
|
+
// Everything inside the window ui/boot/shell.qml maps, arriving by file: URL on the first frame
|
|
19
|
+
// because that is the only way Qt caches it; see AGENTS.md "The first window".
|
|
20
|
+
Rectangle {
|
|
21
|
+
id: view
|
|
22
|
+
anchors.fill: parent
|
|
23
|
+
// On glass each plane draws its own ground, so this one would put a second plane under every pixel.
|
|
24
|
+
color: Glass.on ? "transparent" : Theme.color.background
|
|
25
|
+
// The window this body was loaded into. Named host, not fleaWindow: a root property of
|
|
26
|
+
// that name would bind to itself through Ipc and read null.
|
|
27
|
+
property var host
|
|
28
|
+
readonly property bool dualMode: ViewState.state.view === "dual"
|
|
29
|
+
property int focusSide: (ViewState.state.dual || {}).focus === 1 ? 1 : 0
|
|
30
|
+
// dual.split: the left listing's share of the room right of the rail in ten-thousandths, 5000 the equal split.
|
|
31
|
+
readonly property int dualSplitScale: 10000
|
|
32
|
+
readonly property int dualEqualSplit: 5000
|
|
33
|
+
readonly property real dualRoom: view.width - primaryPane.railInset - Theme.spacing.hairline
|
|
34
|
+
readonly property real dualSplit: typeof (ViewState.state.dual || {}).split === "number" ? ViewState.state.dual.split : view.dualEqualSplit
|
|
35
|
+
readonly property real dualShare: view.dualShareOf(view.dualSplit)
|
|
36
|
+
// False when the room fits no two narrowest listings: nothing moves there, so a drag writes nothing.
|
|
37
|
+
readonly property bool dualFits: Theme.dualMinWidth / view.dualRoom <= 0.5
|
|
38
|
+
// Held between the narrowest listing and its mirror, equal when the room fits no two; the stored split is never rewritten by it.
|
|
39
|
+
function dualShareOf(split) {
|
|
40
|
+
var low = Theme.dualMinWidth / view.dualRoom
|
|
41
|
+
return low > 0.5 ? 0.5 : Math.min(1 - low, Math.max(low, split / view.dualSplitScale))
|
|
42
|
+
}
|
|
43
|
+
// The run card, null until the first ask builds it for the window and keeps it.
|
|
44
|
+
property var runCard: null
|
|
45
|
+
function runLane() {
|
|
46
|
+
if (!view.runCard) view.runCard = Qt.createComponent("RunCard.qml").createObject(view, { z: 2 })
|
|
47
|
+
return view.runCard
|
|
48
|
+
}
|
|
49
|
+
function askRunCard(pane, info) { view.runLane().open(info, pane) }
|
|
50
|
+
// Null until the first Quick Look opens, see previewLoader below.
|
|
51
|
+
readonly property var quickLook: previewLoader.item
|
|
52
|
+
readonly property bool quickLookActive: view.quickLook !== null && view.quickLook.active
|
|
53
|
+
// QuickLookWarm, built by the cursor's first landing on a file and kept: a move off it must not drop the compiled units. A Markdown file builds it in that turn, any other file off the key once a key has moved the cursor, so a launch builds none.
|
|
54
|
+
readonly property var cursorFile: view.currentPane.cursorRow
|
|
55
|
+
onCursorFileChanged: {
|
|
56
|
+
var markdown = Prepared.isMarkdownRow(view.cursorFile)
|
|
57
|
+
if (warmLoader.active || !view.cursorFile || view.cursorFile.d || !(markdown || view.currentPane.inputAt > 0)) return
|
|
58
|
+
warmLoader.asynchronous = !markdown; warmLoader.active = true
|
|
59
|
+
}
|
|
60
|
+
Loader { id: warmLoader; active: false; source: "QuickLookWarm.qml"; onLoaded: item.pane = Qt.binding(function () { return view.currentPane }) }
|
|
61
|
+
readonly property var currentPane: dualMode && focusSide === 1 && secondPane.item ? secondPane.item.pane : primaryPane
|
|
62
|
+
property bool initialized: false
|
|
63
|
+
property bool closing: false
|
|
64
|
+
// Built once per window on first use, so the flasher stays out of the first frame.
|
|
65
|
+
property var flashHost: null
|
|
66
|
+
function openFlash(pane, image, device) {
|
|
67
|
+
if (!view.flashHost)
|
|
68
|
+
view.flashHost = Qt.createComponent("FlashHost.qml").createObject(view, { z: 2 })
|
|
69
|
+
view.flashHost.openFor(pane, image, device)
|
|
70
|
+
}
|
|
71
|
+
function closeFlash() {
|
|
72
|
+
if (view.flashHost)
|
|
73
|
+
view.flashHost.close()
|
|
74
|
+
}
|
|
75
|
+
property var drained: [false, false]
|
|
76
|
+
|
|
77
|
+
function focusPane(side) {
|
|
78
|
+
if (!dualMode) return
|
|
79
|
+
focusSide = side
|
|
80
|
+
// The pane left behind gets its list back, so the ring resumes from the list and never from a rail, bar or strip stop the user left (Shift+Tab from the right lands on the left list, not the rail).
|
|
81
|
+
if (side === 1) primaryPane.focusView = Focus.LIST
|
|
82
|
+
else if (secondPane.item) secondPane.item.pane.focusView = Focus.LIST
|
|
83
|
+
currentPane.forceActiveFocus()
|
|
84
|
+
Qt.callLater(view.rememberDual)
|
|
85
|
+
}
|
|
86
|
+
function switchToLeftRail() { focusPane(0); primaryPane.focusView = Focus.RAIL }
|
|
87
|
+
function rememberPaths() {
|
|
88
|
+
// Path and tab changes in one turn share the deferred navigation patch.
|
|
89
|
+
if (initialized && !dualMode && primaryPane.path)
|
|
90
|
+
view.queueTabStrip()
|
|
91
|
+
if (!initialized || !dualMode || !secondPane.item || !primaryPane.path || !secondPane.item.pane.path) return
|
|
92
|
+
Qt.callLater(view.rememberDual)
|
|
93
|
+
}
|
|
94
|
+
// Strip changes coalesce; an in-flight navigation waits for its path and rows to land before saving.
|
|
95
|
+
property bool tabStripQueued: false
|
|
96
|
+
// Stage trace, on only with FLEA_TRACE_TABDRAG=1; read once, silent otherwise.
|
|
97
|
+
readonly property bool tabTrace: Quickshell.env("FLEA_TRACE_TABDRAG") === "1"
|
|
98
|
+
function traceTab(stage, detail) { if (view.tabTrace) console.log("TABDRAG " + stage + " pid=" + Quickshell.processId + " " + detail) }
|
|
99
|
+
function queueTabStrip() {
|
|
100
|
+
if (view.tabStripQueued)
|
|
101
|
+
return
|
|
102
|
+
view.tabStripQueued = true
|
|
103
|
+
Qt.callLater(function() { view.tabStripQueued = false; view.rememberTabStrip() })
|
|
104
|
+
}
|
|
105
|
+
function rememberTabStrip() {
|
|
106
|
+
if (initialized && !dualMode && primaryPane.path && !primaryPane.listInFlight)
|
|
107
|
+
ViewState.rememberNavigation(primaryPane.path, Tabs.remembered(primaryPane))
|
|
108
|
+
}
|
|
109
|
+
// The mode binding reads ViewState.state; its handlers must finish before persistence replaces it.
|
|
110
|
+
function rememberDual() {
|
|
111
|
+
if (!initialized || !dualMode) return
|
|
112
|
+
var saved = {focus: focusSide}
|
|
113
|
+
if (secondPane.item && primaryPane.path && secondPane.item.pane.path)
|
|
114
|
+
saved.paths = [primaryPane.path, secondPane.item.pane.path]
|
|
115
|
+
ViewState.changeLeaf("dual", saved)
|
|
116
|
+
}
|
|
117
|
+
function quitBackends() {
|
|
118
|
+
if (closing) return
|
|
119
|
+
closing = true
|
|
120
|
+
drained = [false, secondPane.item === null]
|
|
121
|
+
backend.quit()
|
|
122
|
+
if (secondPane.item) secondPane.item.backend.quit()
|
|
123
|
+
}
|
|
124
|
+
function backendDrained(side) {
|
|
125
|
+
var next = drained.slice()
|
|
126
|
+
next[side] = true
|
|
127
|
+
drained = next
|
|
128
|
+
// Finish quitReady delivery before SIGTERM can interrupt another observer.
|
|
129
|
+
if (closing && drained[0] && drained[1]) Qt.callLater(view.finishQuit)
|
|
130
|
+
}
|
|
131
|
+
function finishQuit() {
|
|
132
|
+
Quickshell.execDetached(["kill", String(Quickshell.processId)])
|
|
133
|
+
}
|
|
134
|
+
onDualModeChanged: {
|
|
135
|
+
if (!initialized) return
|
|
136
|
+
if (!dualMode) { focusSide = 0; primaryPane.forceActiveFocus() }
|
|
137
|
+
else if (initialized) { focusPane(focusSide); rememberPaths() }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
// Issue 9's chords, aliased by keys.toml onto the Display section's own text size. The
|
|
142
|
+
// panel writes ui/ViewState.qml directly and shows the value in the row; a chord has no
|
|
143
|
+
// readout of its own with the panel shut, so this one adds the status line.
|
|
144
|
+
function applyTextSize(direction) {
|
|
145
|
+
if (direction === 0)
|
|
146
|
+
ViewState.followTextSize()
|
|
147
|
+
else
|
|
148
|
+
ViewState.stepTextSize(direction)
|
|
149
|
+
view.currentPane.message(TextSize.announce(ViewState.textSize, ViewState.omarchyBase), false)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The backend is told first and answers when it has drained, so closing never leaves a half
|
|
153
|
+
// file; the last-window handler itself is in ui/boot/shell.qml, which always exists.
|
|
154
|
+
Connections { target: backend; function onQuitReady() { view.backendDrained(0) } }
|
|
155
|
+
|
|
156
|
+
Backend {
|
|
157
|
+
id: backend
|
|
158
|
+
preserveSort: view.dualMode
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Built with the window and outliving the rail, so hiding the rail mid-mount kills no wait.
|
|
162
|
+
Flea.NetworkMounts {
|
|
163
|
+
id: networkHost
|
|
164
|
+
backend: primaryPane.backend
|
|
165
|
+
origin: primaryPane
|
|
166
|
+
savedFavourites: Favourites.records
|
|
167
|
+
}
|
|
168
|
+
readonly property var networkService: networkHost
|
|
169
|
+
function ensureNetworkService() {
|
|
170
|
+
return networkHost
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The canvas's own top chrome: where you are on the left, how you are looking at it on
|
|
174
|
+
// the right. The path lives here, which is why the status bar below carries counts instead.
|
|
175
|
+
Flea.ChromeBar {
|
|
176
|
+
id: chrome
|
|
177
|
+
inputLive: !view.quickLookActive
|
|
178
|
+
visible: view.dualMode || (!view.currentPane.trash.opened && !view.currentPane.flashActive)
|
|
179
|
+
height: visible ? Theme.chromeHeight : 0
|
|
180
|
+
anchors.left: parent.left
|
|
181
|
+
anchors.right: parent.right
|
|
182
|
+
anchors.top: parent.top
|
|
183
|
+
// A history is a location and not a directory, so the chrome says its own name.
|
|
184
|
+
path: view.currentPane.flashActive ? "Flash image"
|
|
185
|
+
: view.currentPane.trash.opened ? "trash:///"
|
|
186
|
+
: view.currentPane.recentMode === "trash" ? "trash:///" : view.currentPane.recentMode.length > 0 ? "Recent"
|
|
187
|
+
: view.currentPane.path
|
|
188
|
+
// True only when the drawn path's listing failed, so the bar retries it and nothing else.
|
|
189
|
+
pathFailed: Nav.pathFailed(view.currentPane)
|
|
190
|
+
home: view.currentPane.home
|
|
191
|
+
pathAlias: view.currentPane.pathAlias
|
|
192
|
+
dropPane: view.currentPane
|
|
193
|
+
stopFocused: view.currentPane.focusView === Focus.CHROME
|
|
194
|
+
canGoBack: view.currentPane.canGoBack
|
|
195
|
+
canGoUp: view.currentPane.canGoUp
|
|
196
|
+
viewMode: view.dualMode ? "dual" : view.currentPane.viewMode
|
|
197
|
+
showPath: !view.dualMode
|
|
198
|
+
showHidden: view.currentPane.showHidden
|
|
199
|
+
// flea-qml: Filter stands over Find's results as over a folder's, so it is always available.
|
|
200
|
+
canFilter: true
|
|
201
|
+
canSort: view.currentPane.searchMode.length === 0
|
|
202
|
+
onBackRequested: view.currentPane.goBack()
|
|
203
|
+
onUpRequested: view.currentPane.openParent()
|
|
204
|
+
// flea-qml: each press flips its own toggle, leaving the other as it stands.
|
|
205
|
+
filterOn: view.currentPane.filterTyping || view.currentPane.filterQuery.length > 0
|
|
206
|
+
searchOn: view.currentPane.searchMode.length > 0
|
|
207
|
+
onSearchRequested: view.currentPane.toggleFind()
|
|
208
|
+
onFilterRequested: view.currentPane.toggleFilter()
|
|
209
|
+
onSortRequested: view.currentPane.act("sortNext")
|
|
210
|
+
// The primary pane is the one the window keeps after Dual, so every view choice, the secondary pane's keys included, lands there.
|
|
211
|
+
onViewChosen: function (mode) { primaryPane.chooseView(mode) }
|
|
212
|
+
// The path bar's four. The primaryPane navigates and answers for the keyboard exactly as it
|
|
213
|
+
// does for every other route in, so a path typed and a row opened end the same way.
|
|
214
|
+
// Issue 194: a typed smb:// (or other network) address mounts through the window-long
|
|
215
|
+
// host's own open-a-share path, which lists a server's shares, rather than listing a
|
|
216
|
+
// local path. A network address is never listed as a folder: with no host it is refused
|
|
217
|
+
// with a sentence, the way 0.3.5 refused the line.
|
|
218
|
+
onPathEntered: function (path) {
|
|
219
|
+
if (path === "trash:///") { view.currentPane.trash.open(); return }
|
|
220
|
+
if (/^(smb|sftp|ftp|ftps|dav|davs|nfs|afp):\/\//i.test(path)) {
|
|
221
|
+
var target = view.currentPane
|
|
222
|
+
var service = target ? target.ensureNetworkService() : null
|
|
223
|
+
if (service) service.openShare(path, false, "", false, { origin: target })
|
|
224
|
+
else target.message("That network address cannot be opened here.", true)
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
view.currentPane.open(path)
|
|
228
|
+
}
|
|
229
|
+
onEditClosed: view.currentPane.forceActiveFocus()
|
|
230
|
+
// Tab peeks in name order with dotfiles first; the columns view asked nothing, so it drops the reply.
|
|
231
|
+
onCompleteRequested: function (dir, hidden) { view.currentPane.backend.peek(dir, view.currentPane.windowSize, hidden, false) }
|
|
232
|
+
onJumpRequested: function (id, ranking, favourites, recent) { view.currentPane.backend.jump(id, ranking, favourites, recent) }
|
|
233
|
+
onSaid: function (text) { bar.say(text, false) }
|
|
234
|
+
onSettingsRequested: settingsPanel.open(view.currentPane)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// The peek behind Tab. Every peeked line carries the directory and the hidden flag it
|
|
238
|
+
// answers for, so the bar takes the reply to its own request and the columns view, which
|
|
239
|
+
// peeks the same wire for the primaryPane's ancestors, goes on taking its own.
|
|
240
|
+
Connections {
|
|
241
|
+
target: view.currentPane.backend
|
|
242
|
+
function onPeeked(path, hidden, total, rows, readFailed, mode) { chrome.completeWith(path, hidden, rows) }
|
|
243
|
+
// A bar never opened sent no ask, so an answer with no jump is dropped like a stale one.
|
|
244
|
+
function onJumped(id, favourites, zoxide, recent, frecency) { if (chrome.jump !== null) chrome.jump.take(id, favourites, zoxide, recent, frecency) }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Flea.TabBar {
|
|
248
|
+
id: tabBar
|
|
249
|
+
anchors.left: parent.left
|
|
250
|
+
anchors.right: parent.right
|
|
251
|
+
anchors.top: chrome.bottom
|
|
252
|
+
pane: view.currentPane
|
|
253
|
+
stopFocused: view.currentPane.focusView === Focus.STRIP
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Load the acknowledgment handler from the boot URL to preserve startup without ui/qmldir.
|
|
257
|
+
Loader {
|
|
258
|
+
id: tabAck
|
|
259
|
+
active: true
|
|
260
|
+
source: "file://" + Quickshell.shellDir + "/fleatab.qml"
|
|
261
|
+
onLoaded: {
|
|
262
|
+
item.tabBar = tabBar
|
|
263
|
+
item.view = view
|
|
264
|
+
item.tabs = Tabs
|
|
265
|
+
item.panes = Qt.binding(function () { return [primaryPane, secondPane.item ? secondPane.item.pane : null] })
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// A hidden lone-tab strip still needs a pointer-transparent receiver with the strip's validation.
|
|
270
|
+
DropArea {
|
|
271
|
+
anchors.fill: parent
|
|
272
|
+
keys: [Tabs.TAB_MIME]
|
|
273
|
+
enabled: !view.dualMode && !(view.currentPane.tabs && view.currentPane.tabs.items
|
|
274
|
+
&& view.currentPane.tabs.items.length > 1)
|
|
275
|
+
onEntered: function (drag) {
|
|
276
|
+
var ok = Tabs.enterAccepts(drag.formats, drag.getDataAsString(Tabs.TAB_MIME), undefined, Tabs.canReceive(view.currentPane), false)
|
|
277
|
+
view.traceTab("enter-window", "formats=" + String(drag.formats) + " ok=" + ok)
|
|
278
|
+
if (!ok)
|
|
279
|
+
drag.accepted = false
|
|
280
|
+
}
|
|
281
|
+
onExited: view.traceTab("leave-window", "")
|
|
282
|
+
onDropped: function (drop) {
|
|
283
|
+
var payload = drop.getDataAsString(Tabs.TAB_MIME)
|
|
284
|
+
view.traceTab("drop-window", "empty=" + (payload.length === 0) + " len=" + payload.length)
|
|
285
|
+
var info = Tabs.parseTabMime(payload)
|
|
286
|
+
if (!info) {
|
|
287
|
+
view.traceTab("drop-skip", "reason=window-bad-payload")
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
if (Tabs.isOwnTab(info)) {
|
|
291
|
+
view.traceTab("drop-skip", "reason=window-own-tab")
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
// The take decision answers Move at once; the peek behind it may still refuse, and then no ack goes out.
|
|
295
|
+
if (Tabs.dropDecision(info, undefined, false, Tabs.canReceive(view.currentPane)) !== Tabs.DROP_TAKE) {
|
|
296
|
+
view.traceTab("drop-skip", "reason=window-decision-ignore canReceive=" + Tabs.canReceive(view.currentPane))
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
drop.accept(Qt.MoveAction)
|
|
300
|
+
tabBar.acceptTabDrop(payload, info, -1)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Flea.Pane {
|
|
305
|
+
id: primaryPane
|
|
306
|
+
anchors.left: parent.left
|
|
307
|
+
width: view.dualMode ? railInset + (view.width - railInset - Theme.spacing.hairline) * view.dualShare : view.width
|
|
308
|
+
anchors.top: tabBar.bottom
|
|
309
|
+
anchors.bottom: bar.top
|
|
310
|
+
backend: backend
|
|
311
|
+
networkService: view.networkService
|
|
312
|
+
dualMode: view.dualMode
|
|
313
|
+
paneFocused: view.currentPane === primaryPane
|
|
314
|
+
railPane: view.currentPane
|
|
315
|
+
chromeBar: chrome
|
|
316
|
+
tabStrip: tabBar
|
|
317
|
+
onFocusRequested: view.focusPane(0)
|
|
318
|
+
onSwitchPane: view.focusPane(1)
|
|
319
|
+
onSwitchToRail: view.switchToLeftRail()
|
|
320
|
+
onPathChanged: view.rememberPaths()
|
|
321
|
+
onListingStateChanged: if (primaryPane.listingState !== "loading") ipcLoader.active = true
|
|
322
|
+
onListInFlightChanged: { if (!primaryPane.listInFlight) view.queueTabStrip() }
|
|
323
|
+
onTabsChanged: view.queueTabStrip()
|
|
324
|
+
onClipboardChanged: if (secondPane.item && secondPane.item.pane.clipboard !== clipboard) secondPane.item.pane.clipboard = clipboard
|
|
325
|
+
overlayParent: view
|
|
326
|
+
preview: view.quickLook
|
|
327
|
+
previewLoader: previewLoader
|
|
328
|
+
shareBrowser: shareBrowser
|
|
329
|
+
keymapSheet: keymapSheet
|
|
330
|
+
settingsPanel: settingsPanel
|
|
331
|
+
statusBar: bar
|
|
332
|
+
onMessage: function (text, isError) { bar.say(text, isError) }
|
|
333
|
+
onArmPrompted: function (text) { bar.sayArm(text) }
|
|
334
|
+
onForgetMessage: function (text) { bar.forget(text) }
|
|
335
|
+
onOperationResult: function (headline, detail, isError) { bar.say(headline, isError, detail) }
|
|
336
|
+
// A running operation's line, which stands until the operation replaces it; see ui/StatusBar.qml.
|
|
337
|
+
onSticky: function (text) { bar.setActivity(primaryPane, text, primaryPane.transfer) }
|
|
338
|
+
onStickyStarting: function (text) { bar.setActivity(primaryPane, text, primaryPane.transfer, true) }
|
|
339
|
+
onConvertRequested: function (name) { convertDialog.open(name, primaryPane) }
|
|
340
|
+
// Archives040: compress opens over resolved paths, extract over the named archive.
|
|
341
|
+
onCompressPasswordRequested: function (paths, menuId) {
|
|
342
|
+
view.openArchiveCards().askCompressPassword(primaryPane, paths, menuId)
|
|
343
|
+
}
|
|
344
|
+
onExtractPasswordRequested: function (path, dest, err) { view.openArchiveCards().openExtractPassword({ path: path, dest: dest }, primaryPane, err) }
|
|
345
|
+
onPermissionsRequested: function (path) { permissionsDialog.open(path, primaryPane) }
|
|
346
|
+
// Permissions040: the whole selection's paths for the multi-row card.
|
|
347
|
+
onPermissionsBatchRequested: function (paths) { permissionsDialog.openMany(paths, primaryPane) }
|
|
348
|
+
// Bulk rename takes the whole selection's paths for its card.
|
|
349
|
+
onBulkRenameRequested: function (paths) { view.openBulkRename(paths, primaryPane) }
|
|
350
|
+
onPathBarRequested: chrome.startEdit()
|
|
351
|
+
// Issue 9. ViewState persists the stop and Theme derives its own tokens from it, so
|
|
352
|
+
// the whole window follows without any surface reading the chord itself.
|
|
353
|
+
onTextSizeRequested: function (direction) { view.applyTextSize(direction) }
|
|
354
|
+
onOpened: function (path) { if (shareBrowser.owner === primaryPane) shareBrowser.close() }
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
Loader {
|
|
358
|
+
id: secondPane
|
|
359
|
+
anchors { left: primaryPane.right; right: parent.right; top: tabBar.bottom; bottom: bar.top }
|
|
360
|
+
anchors.leftMargin: Theme.spacing.hairline
|
|
361
|
+
property bool built: false
|
|
362
|
+
active: view.dualMode || built
|
|
363
|
+
visible: view.dualMode
|
|
364
|
+
sourceComponent: Item {
|
|
365
|
+
readonly property alias pane: otherPane
|
|
366
|
+
readonly property alias backend: otherBackend
|
|
367
|
+
Backend {
|
|
368
|
+
id: otherBackend
|
|
369
|
+
preserveSort: true
|
|
370
|
+
onQuitReady: view.backendDrained(1)
|
|
371
|
+
}
|
|
372
|
+
Flea.Pane {
|
|
373
|
+
id: otherPane
|
|
374
|
+
anchors.fill: parent
|
|
375
|
+
backend: otherBackend
|
|
376
|
+
clipboardState: primaryPane.clipboardState
|
|
377
|
+
sharedSidebar: primaryPane.sidebar
|
|
378
|
+
chromeBar: chrome
|
|
379
|
+
tabStrip: tabBar
|
|
380
|
+
networkService: view.networkService
|
|
381
|
+
sharedNetworkService: primaryPane.networkService
|
|
382
|
+
dualMode: true
|
|
383
|
+
listOnly: true
|
|
384
|
+
paneFocused: view.currentPane === otherPane
|
|
385
|
+
overlayParent: view
|
|
386
|
+
preview: primaryPane.preview
|
|
387
|
+
previewLoader: primaryPane.previewLoader
|
|
388
|
+
shareBrowser: primaryPane.shareBrowser
|
|
389
|
+
keymapSheet: primaryPane.keymapSheet
|
|
390
|
+
settingsPanel: primaryPane.settingsPanel
|
|
391
|
+
statusBar: bar
|
|
392
|
+
onFocusRequested: view.focusPane(1)
|
|
393
|
+
onViewRequested: function (mode) { chrome.viewChosen(mode) }
|
|
394
|
+
onSwitchPane: view.focusPane(0)
|
|
395
|
+
onSwitchToRail: view.switchToLeftRail()
|
|
396
|
+
onPathChanged: view.rememberPaths()
|
|
397
|
+
onClipboardChanged: if (primaryPane.clipboard !== clipboard) primaryPane.clipboard = clipboard
|
|
398
|
+
onMessage: function(text, error) { bar.say(text, error) }
|
|
399
|
+
onArmPrompted: function(text) { bar.sayArm(text) }
|
|
400
|
+
onOperationResult: function(headline, detail, error) { bar.say(headline, error, detail) }
|
|
401
|
+
onSticky: function(text) { bar.setActivity(otherPane, text, otherPane.transfer) }
|
|
402
|
+
onStickyStarting: function(text) { bar.setActivity(otherPane, text, otherPane.transfer, true) }
|
|
403
|
+
onConvertRequested: function(name) { convertDialog.open(name, otherPane) }
|
|
404
|
+
onCompressPasswordRequested: function (paths, menuId) {
|
|
405
|
+
view.openArchiveCards().askCompressPassword(otherPane, paths, menuId)
|
|
406
|
+
}
|
|
407
|
+
onExtractPasswordRequested: function (path, dest, err) { view.openArchiveCards().openExtractPassword({ path: path, dest: dest }, otherPane, err) }
|
|
408
|
+
onPermissionsRequested: function(path) { permissionsDialog.open(path, otherPane) }
|
|
409
|
+
onPermissionsBatchRequested: function (paths) { permissionsDialog.openMany(paths, otherPane) }
|
|
410
|
+
onBulkRenameRequested: function (paths) { view.openBulkRename(paths, otherPane) }
|
|
411
|
+
onPathBarRequested: chrome.startEdit()
|
|
412
|
+
onTextSizeRequested: function(direction) { view.applyTextSize(direction) }
|
|
413
|
+
onOpened: if (otherPane.shareBrowser.owner === otherPane) otherPane.shareBrowser.close()
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
onLoaded: {
|
|
417
|
+
built = true
|
|
418
|
+
// Before initialized this is the launch, the only time a command-line folder applies.
|
|
419
|
+
var named = view.initialized ? "" : (Quickshell.env("FLEA_PATH") || "")
|
|
420
|
+
var pair = Startup.dualPaths(ViewState.state.dual, primaryPane.path || primaryPane.home, named)
|
|
421
|
+
item.pane.clipboard = primaryPane.clipboard
|
|
422
|
+
if (pair.launchSide === 1) {
|
|
423
|
+
item.pane.pendingSelect = Quickshell.env("FLEA_SELECT") || ""
|
|
424
|
+
Tabs.prepareCursor(item.pane, named ? Quickshell.env("FLEA_TAB_CURSOR") : "")
|
|
425
|
+
}
|
|
426
|
+
item.pane.open(pair.paths[1])
|
|
427
|
+
if (view.initialized && view.dualMode) view.focusPane(view.focusSide)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// The zone starts on the divider pixel and runs right over the second listing's row padding, so the first listing keeps its scroll lane whole.
|
|
432
|
+
Loader {
|
|
433
|
+
x: primaryPane.width
|
|
434
|
+
y: primaryPane.y
|
|
435
|
+
active: view.dualMode
|
|
436
|
+
sourceComponent: Item {
|
|
437
|
+
id: divider
|
|
438
|
+
property bool dragging: false
|
|
439
|
+
property bool moved: false
|
|
440
|
+
property real startX: 0
|
|
441
|
+
property real dx: 0
|
|
442
|
+
readonly property real leftWidth: primaryPane.width - primaryPane.railInset
|
|
443
|
+
width: handle.width
|
|
444
|
+
height: primaryPane.height
|
|
445
|
+
function shareAt(travel) { return view.dualShareOf(view.dualSplitScale * (divider.leftWidth + travel) / view.dualRoom) }
|
|
446
|
+
function begin(mouse) {
|
|
447
|
+
divider.startX = handle.mapToItem(view, mouse.x, mouse.y).x
|
|
448
|
+
divider.dx = 0
|
|
449
|
+
divider.moved = false
|
|
450
|
+
divider.dragging = true
|
|
451
|
+
}
|
|
452
|
+
// A press that never travels is a click and writes nothing; the accent line follows the pointer, held to the clamp.
|
|
453
|
+
function move(mouse) {
|
|
454
|
+
if (!divider.dragging) return
|
|
455
|
+
var travel = handle.mapToItem(view, mouse.x, mouse.y).x - divider.startX
|
|
456
|
+
if (!divider.moved && Math.abs(travel) < 1) return
|
|
457
|
+
divider.moved = true
|
|
458
|
+
divider.dx = view.dualRoom * divider.shareAt(travel) - divider.leftWidth
|
|
459
|
+
}
|
|
460
|
+
// One leaf on release, so both listings re-lay from it in the same frame.
|
|
461
|
+
function end() {
|
|
462
|
+
if (!divider.dragging) return
|
|
463
|
+
if (divider.moved && view.dualFits) ViewState.changeLeaf("dual", {split: Math.round(view.dualSplitScale * divider.shareAt(divider.dx))})
|
|
464
|
+
divider.reset()
|
|
465
|
+
}
|
|
466
|
+
function reset() { divider.dragging = false; divider.moved = false; divider.dx = 0 }
|
|
467
|
+
Rectangle {
|
|
468
|
+
width: Theme.spacing.hairline
|
|
469
|
+
height: parent.height
|
|
470
|
+
color: Theme.color.mutedFrame
|
|
471
|
+
}
|
|
472
|
+
Flea.ResizeHandle {
|
|
473
|
+
id: handle
|
|
474
|
+
height: parent.height
|
|
475
|
+
lineX: divider.dx
|
|
476
|
+
hot: divider.dragging
|
|
477
|
+
onPressed: function (mouse) { divider.begin(mouse) }
|
|
478
|
+
onMoved: function (mouse) { divider.move(mouse) }
|
|
479
|
+
onCanceled: divider.reset()
|
|
480
|
+
onReleased: divider.end()
|
|
481
|
+
onDoubleClicked: ViewState.changeLeaf("dual", {split: view.dualEqualSplit})
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
Flea.StatusBar {
|
|
487
|
+
id: bar
|
|
488
|
+
anchors.left: parent.left
|
|
489
|
+
anchors.right: parent.right
|
|
490
|
+
anchors.bottom: parent.bottom
|
|
491
|
+
path: view.currentPane.trash.opened ? "trash:///"
|
|
492
|
+
: view.currentPane.path
|
|
493
|
+
total: view.currentPane.flashActive && view.flashHost ? view.flashHost.count
|
|
494
|
+
: view.currentPane.trash.opened ? view.currentPane.trash.total : view.currentPane.total
|
|
495
|
+
// The counts zone names the drives while the flasher owns the pane; disk facts stay the covered folder's.
|
|
496
|
+
flashCount: view.currentPane.flashActive && view.flashHost ? view.flashHost.countText : ""
|
|
497
|
+
listingState: view.currentPane.listingState
|
|
498
|
+
pane: (view.currentPane.trash.opened || view.currentPane.flashActive) ? null : view.currentPane
|
|
499
|
+
filePane: view.currentPane.flashActive ? null : view.currentPane
|
|
500
|
+
armOwner: view.currentPane.trash.opened ? view.currentPane.trash.item : view.currentPane
|
|
501
|
+
selectionCount: view.currentPane.flashActive ? 0
|
|
502
|
+
: view.currentPane.trash.opened ? view.currentPane.trash.selectedCount : view.currentPane.selectionCount()
|
|
503
|
+
fsName: view.currentPane.fsName
|
|
504
|
+
fsFree: view.currentPane.fsFree
|
|
505
|
+
fsPending: !view.currentPane.fsAnswered
|
|
506
|
+
searchRunning: view.currentPane.searchRunning
|
|
507
|
+
searchLine: Search.live(view.currentPane)
|
|
508
|
+
? Search.statusLine(view.currentPane.searchRunning, view.currentPane.total, view.currentPane.searchScanned, view.currentPane.searchMs)
|
|
509
|
+
: ""
|
|
510
|
+
retryLine: view.currentPane.trash.opened || view.currentPane.flashActive ? "" : view.currentPane.retrySelectionText
|
|
511
|
+
onTransferCancelRequested: function (id) {
|
|
512
|
+
// A flash cancels through its own request: only Cancel stops one, wherever the card sits.
|
|
513
|
+
if (bar.transfer.flash === true) bar.transferOwner.backend.flashCancel(id)
|
|
514
|
+
else bar.transferOwner.backend.transfercancel(id)
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Quick Look is built by the first Space, so a window that never previews pays nothing for it; Pane.quickLook() turns it on.
|
|
519
|
+
Loader {
|
|
520
|
+
id: previewLoader
|
|
521
|
+
anchors.fill: parent
|
|
522
|
+
z: 1
|
|
523
|
+
active: false
|
|
524
|
+
source: "Preview.qml"
|
|
525
|
+
onLoaded: item.pane = Qt.binding(function () { return view.currentPane })
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
MouseArea {
|
|
529
|
+
anchors.fill: parent
|
|
530
|
+
z: 4
|
|
531
|
+
visible: primaryPane.trash.confirming || (secondPane.item && secondPane.item.pane.trash.confirming)
|
|
532
|
+
hoverEnabled: true
|
|
533
|
+
acceptedButtons: Qt.AllButtons
|
|
534
|
+
onWheel: function(wheel) { wheel.accepted = true }
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Every overlay below is built by its first open and kept, see AGENTS.md rule 6: a launch
|
|
538
|
+
// that never opens one pays neither its compile nor its objects. Each Loader carries the
|
|
539
|
+
// one or two members its callers read, and ui/Ipc.qml reads the built item or null.
|
|
540
|
+
// Each Loader carries its item's z, because a z set inside the item orders it only within
|
|
541
|
+
// the Loader, and the share browser below would otherwise paint over an open card.
|
|
542
|
+
Loader {
|
|
543
|
+
id: convertDialog
|
|
544
|
+
z: 2
|
|
545
|
+
anchors.fill: parent
|
|
546
|
+
active: false
|
|
547
|
+
source: "ConvertDialog.qml"
|
|
548
|
+
readonly property bool opened: item !== null && item.opened
|
|
549
|
+
property var owner: null
|
|
550
|
+
function open(name, holder) { owner = holder; active = true; item.open(name, holder) }
|
|
551
|
+
}
|
|
552
|
+
Connections {
|
|
553
|
+
target: convertDialog.item
|
|
554
|
+
function onAccepted(source, format, strip, requestId) { Ops.convert(convertDialog.owner, source, format, strip, requestId) }
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Archives040: both password cards build on first open, never at startup.
|
|
558
|
+
property var archiveCards: null
|
|
559
|
+
function openArchiveCards() {
|
|
560
|
+
if (!view.archiveCards) {
|
|
561
|
+
var component = Qt.createComponent("ArchiveCards.qml")
|
|
562
|
+
view.archiveCards = component.status === Component.Ready ? component.createObject(view, { z: 2 }) : null
|
|
563
|
+
if (!view.archiveCards) throw new Error("ArchiveCards.qml did not build: " + component.errorString())
|
|
564
|
+
}
|
|
565
|
+
return view.archiveCards
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
Loader {
|
|
569
|
+
id: permissionsDialog
|
|
570
|
+
z: 2
|
|
571
|
+
anchors.fill: parent
|
|
572
|
+
active: false
|
|
573
|
+
source: "PermissionsDialog.qml"
|
|
574
|
+
readonly property bool opened: item !== null && item.opened
|
|
575
|
+
property var owner: null
|
|
576
|
+
function open(path, holder) { owner = holder; active = true; item.open(path, holder.listArea) }
|
|
577
|
+
function openMany(paths, holder) { owner = holder; active = true; item.openMany(paths, holder.listArea) }
|
|
578
|
+
}
|
|
579
|
+
Connections {
|
|
580
|
+
target: permissionsDialog.item
|
|
581
|
+
function onRequested(message) { permissionsDialog.owner.backend.send(message) }
|
|
582
|
+
function onChanged(note) { permissionsDialog.owner.refresh("", true); bar.say(note && note.length > 0 ? note : "Permissions changed.", false) }
|
|
583
|
+
function onRefreshNeeded() { permissionsDialog.owner.refresh("", true) }
|
|
584
|
+
}
|
|
585
|
+
// The bulk card is built once per window on its first multi-row Rename, with no placeholder before that.
|
|
586
|
+
property var bulkRenameDialog: null
|
|
587
|
+
function openBulkRename(paths, holder) {
|
|
588
|
+
if (!view.bulkRenameDialog) view.bulkRenameDialog = Qt.createComponent("BulkRenameDialog.qml").createObject(view, { z: 2 })
|
|
589
|
+
view.bulkRenameDialog.open(paths, holder)
|
|
590
|
+
}
|
|
591
|
+
Connections {
|
|
592
|
+
target: permissionsDialog.owner ? permissionsDialog.owner.backend : null
|
|
593
|
+
function onPermissionsResult(message) {
|
|
594
|
+
if (permissionsDialog.item) permissionsDialog.item.receive(message)
|
|
595
|
+
}
|
|
596
|
+
function onFailed(where, input, message, mode) {
|
|
597
|
+
if (where === "backend" && permissionsDialog.item) permissionsDialog.item.backendFailed()
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// The keymap sheet ? opens, over the whole window as the convert popup is.
|
|
602
|
+
Loader {
|
|
603
|
+
id: keymapSheet
|
|
604
|
+
z: 2
|
|
605
|
+
anchors.fill: parent
|
|
606
|
+
active: false
|
|
607
|
+
source: "KeymapSheet.qml"
|
|
608
|
+
readonly property bool opened: item !== null && item.opened
|
|
609
|
+
function open(holder) { active = true; item.open(holder) }
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// The settings panel, reached by the comma key from either view, by the toolbar's sliders
|
|
613
|
+
// button, and by the third door the Settings board draws: the background menu's own
|
|
614
|
+
// Settings row, which ui/js/Menu.js backgroundEntries builds and ui/Pane.qml act routes.
|
|
615
|
+
Loader {
|
|
616
|
+
id: settingsPanel
|
|
617
|
+
z: 3
|
|
618
|
+
anchors.fill: parent
|
|
619
|
+
active: false
|
|
620
|
+
source: "SettingsPanel.qml"
|
|
621
|
+
readonly property bool opened: item !== null && item.opened
|
|
622
|
+
function open(holder) { active = true; item.open(holder) }
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
Loader {
|
|
626
|
+
id: networkDialog
|
|
627
|
+
z: 2
|
|
628
|
+
anchors.fill: parent
|
|
629
|
+
active: false
|
|
630
|
+
source: "NetworkDialog.qml"
|
|
631
|
+
property var owner: null
|
|
632
|
+
property Item origin: null
|
|
633
|
+
readonly property bool opened: item !== null && item.opened
|
|
634
|
+
function open() {
|
|
635
|
+
if (opened) return
|
|
636
|
+
origin = view.currentPane
|
|
637
|
+
active = true
|
|
638
|
+
item.open()
|
|
639
|
+
}
|
|
640
|
+
function openLocation(uri, label, password, reason, failedConnect, fromPane) {
|
|
641
|
+
if (opened || !fromPane) return
|
|
642
|
+
origin = fromPane
|
|
643
|
+
active = true
|
|
644
|
+
item.openLocation(uri, label, password, reason, failedConnect)
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
Connections {
|
|
648
|
+
target: networkDialog.item
|
|
649
|
+
// FocusScope remembers its own last-focused child, list or rail, and restores it.
|
|
650
|
+
function onClosed() { if (view.currentPane.sidebar) view.currentPane.sidebar.editingPlace = ""; view.currentPane.forceActiveFocus() }
|
|
651
|
+
function onMountRequested(requestId, uri, label, password) {
|
|
652
|
+
if (!networkDialog.origin) {
|
|
653
|
+
networkDialog.item.mountFinished(requestId, uri, false, "The requesting pane is no longer available.", false)
|
|
654
|
+
return
|
|
655
|
+
}
|
|
656
|
+
// The host outlives the rail, so hiding the rail mid-dialog loses no answer: the
|
|
657
|
+
// completed below still lands and the dialog still finishes.
|
|
658
|
+
var sidebar = networkDialog.origin ? networkDialog.origin.sidebar : null
|
|
659
|
+
if (sidebar) RailMenu.placeSubmitted(sidebar, requestId)
|
|
660
|
+
networkDialog.owner = view.ensureNetworkService()
|
|
661
|
+
networkDialog.owner.saveLocation(uri, label, password, requestId, networkDialog.origin)
|
|
662
|
+
}
|
|
663
|
+
function onCancelRequested(requestId) { if (networkDialog.owner) networkDialog.owner.cancelLocation(requestId) }
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// The dialog's answer lands here rather than on the rail, so it lands whether the rail is
|
|
667
|
+
// shown or has been unloaded mid-mount. Every host answer routes once by origin here;
|
|
668
|
+
// ui/PaneRail.qml carries no duplicate handling, so a dual-view open cannot fire twice.
|
|
669
|
+
Connections {
|
|
670
|
+
target: view.networkService
|
|
671
|
+
function onCompleted(requestId, uri, success, reason, connectFailed) {
|
|
672
|
+
var sidebar = view.currentPane.sidebar
|
|
673
|
+
if (sidebar && view.networkService) RailMenu.placeSaved(sidebar, view.networkService, requestId, uri, success)
|
|
674
|
+
if (networkDialog.item) networkDialog.item.mountFinished(requestId, uri, success, reason, connectFailed)
|
|
675
|
+
}
|
|
676
|
+
function onSharesListed(baseUri, baseLabel, names, origin) { if (origin) shareBrowser.open(baseUri, baseLabel, names, origin) }
|
|
677
|
+
function onRetryRequested(uri, label, password, reason, failedConnect, origin) {
|
|
678
|
+
networkDialog.openLocation(uri, label, password, reason, failedConnect, origin)
|
|
679
|
+
}
|
|
680
|
+
function onOpened(path, origin) { if (origin) RailKeys.openFrom(origin, path, origin.sidebar) }
|
|
681
|
+
function onOpenFileRequested(path, origin) { if (origin) origin.openFile(path) }
|
|
682
|
+
function onMessage(text, isError) {
|
|
683
|
+
var pane = view.currentPane
|
|
684
|
+
if (pane.sidebar) RailKeys.messaged(pane.sidebar, isError)
|
|
685
|
+
pane.message(text, isError)
|
|
686
|
+
}
|
|
687
|
+
function onSticky(text, origin) { (origin || view.currentPane).sticky(text) }
|
|
688
|
+
function onStartingBridge(text, origin) { (origin || view.currentPane).stickyStarting(text) }
|
|
689
|
+
function onRenamed() { if (view.currentPane.sidebar) view.currentPane.sidebar.reloadBookmarks() }
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
Connections {
|
|
693
|
+
target: view.currentPane.sidebar
|
|
694
|
+
function onAddRequested() { networkDialog.open() }
|
|
695
|
+
function onEditRequested(uri, label, password, reason, failedConnect, origin) {
|
|
696
|
+
networkDialog.openLocation(uri, label, password, reason, failedConnect, origin)
|
|
697
|
+
}
|
|
698
|
+
function onUnlockRequested(device, label, size, fstype) { view.ensureUnlockHost().openFor(device, label, size, fstype, view.currentPane.sidebar) }
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Unlock (U039): created once on first use and reused after, so no placeholder object stands before it.
|
|
702
|
+
property var unlockHost: null
|
|
703
|
+
function ensureUnlockHost() {
|
|
704
|
+
if (view.unlockHost === null)
|
|
705
|
+
view.unlockHost = Qt.createComponent("UnlockHost.qml").createObject(view, { z: 2, window: view, footer: bar })
|
|
706
|
+
return view.unlockHost
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// A bare Network entry's own shares, same listArea placement as EmptyState above.
|
|
710
|
+
// An Item fronts this Loader because its callers read active, which is a Loader's own load switch.
|
|
711
|
+
Item {
|
|
712
|
+
id: shareBrowser
|
|
713
|
+
property Item owner: primaryPane
|
|
714
|
+
onOwnerChanged: if (!owner) close()
|
|
715
|
+
visible: owner !== null && (owner === primaryPane || view.dualMode)
|
|
716
|
+
x: owner ? (owner === primaryPane ? primaryPane.x : secondPane.x) + owner.listSlot.x : 0
|
|
717
|
+
y: owner ? tabBar.y + tabBar.height + owner.listSlot.y : 0
|
|
718
|
+
width: owner ? owner.listSlot.width : 0
|
|
719
|
+
height: owner ? owner.listSlot.height : 0
|
|
720
|
+
readonly property bool active: shareLoader.item !== null && shareLoader.item.active
|
|
721
|
+
function open(uri, label, names, origin) { owner = origin; shareLoader.active = true; shareLoader.item.open(uri, label, names) }
|
|
722
|
+
function close() { if (shareLoader.item) shareLoader.item.close() }
|
|
723
|
+
// ui/js/Focus.js shareBrowserAct's two other verbs, reached only while the overlay is up.
|
|
724
|
+
function moveCursor(delta) { if (shareLoader.item) shareLoader.item.moveCursor(delta) }
|
|
725
|
+
function activateCursor() { if (shareLoader.item) shareLoader.item.activateCursor() }
|
|
726
|
+
Loader { id: shareLoader; anchors.fill: parent; active: false; source: "ShareBrowser.qml" }
|
|
727
|
+
}
|
|
728
|
+
Connections {
|
|
729
|
+
target: shareLoader.item
|
|
730
|
+
function onClosed() { view.currentPane.forceActiveFocus() }
|
|
731
|
+
function onActivated(uri, label) {
|
|
732
|
+
view.focusPane(shareBrowser.owner === primaryPane ? 0 : 1)
|
|
733
|
+
// The host outlives the rail, so a hidden rail never blocks a share the browser lists.
|
|
734
|
+
var service = shareBrowser.owner.ensureNetworkService()
|
|
735
|
+
if (!service) {
|
|
736
|
+
shareBrowser.owner.message("That network share cannot be opened here.", true)
|
|
737
|
+
return
|
|
738
|
+
}
|
|
739
|
+
service.openChildShare(uri, label, shareBrowser.owner)
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Issue 20: the mouse's side buttons belong to the window, not a row; Nav.js mouseBack and MouseNav.js forward decide, and the overlays below refuse both.
|
|
744
|
+
TapHandler {
|
|
745
|
+
acceptedButtons: Qt.BackButton | Qt.ForwardButton
|
|
746
|
+
onTapped: function (eventPoint, button) {
|
|
747
|
+
if (view.currentPane.menuActions.opened || settingsPanel.opened || view.currentPane.trash.confirming || chrome.editing || convertDialog.opened || permissionsDialog.opened || (view.bulkRenameDialog !== null && view.bulkRenameDialog.opened) || keymapSheet.opened
|
|
748
|
+
|| networkDialog.opened || (view.unlockHost !== null && view.unlockHost.opened) || (shareBrowser.active && shareBrowser.owner === view.currentPane) || view.quickLookActive
|
|
749
|
+
|| view.currentPane.renameEditor() !== null || (view.currentPane.sidebar && view.currentPane.sidebar.renameEditor() !== null))
|
|
750
|
+
return
|
|
751
|
+
if (button === Qt.ForwardButton) MouseNav.forward(view.currentPane)
|
|
752
|
+
else if (view.currentPane.flashActive) view.closeFlash()
|
|
753
|
+
else if (view.currentPane.trash.opened) view.currentPane.trash.close()
|
|
754
|
+
else Nav.mouseBack(view.currentPane)
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// The block view's diagrams are drawn by a window's backend (ui/FigureService.qml).
|
|
759
|
+
Component.onDestruction: FigureService.release(backend)
|
|
760
|
+
|
|
761
|
+
Component.onCompleted: {
|
|
762
|
+
FigureService.adopt(backend)
|
|
763
|
+
var home = Quickshell.env("HOME")
|
|
764
|
+
var named = Quickshell.env("FLEA_PATH") || ""
|
|
765
|
+
var start = Startup.startPath(ViewState.state, home, named)
|
|
766
|
+
var pair = Startup.dualPaths(ViewState.state.dual, start, named)
|
|
767
|
+
// Read once, and only on the side that took the named folder; Pane.applyPendingSelect() forgets it after the first rows.
|
|
768
|
+
if (!view.dualMode || pair.launchSide !== 1) {
|
|
769
|
+
primaryPane.pendingSelect = Quickshell.env("FLEA_SELECT") || ""
|
|
770
|
+
Tabs.prepareCursor(primaryPane, named ? Quickshell.env("FLEA_TAB_CURSOR") : "")
|
|
771
|
+
}
|
|
772
|
+
// Tabs040 callout 2: Last folder reopens every remembered tab in order at its folder.
|
|
773
|
+
// Dual startup keeps its own pair, and a named path outranks the strip either way.
|
|
774
|
+
var plan = view.dualMode ? null : Tabs.restorePlan(ViewState.state, named)
|
|
775
|
+
if (plan)
|
|
776
|
+
primaryPane.tabs = Tabs.pack(Tabs.restoreItems(primaryPane, plan.paths), plan.index)
|
|
777
|
+
primaryPane.open(plan ? plan.paths[plan.index] : (view.dualMode ? pair.paths[0] : start))
|
|
778
|
+
view.initialized = true
|
|
779
|
+
// flea --unlock opens the same dialog a rail row opens, for scripts and a udev rule.
|
|
780
|
+
var unlockDevice = Quickshell.env("FLEA_UNLOCK") || ""
|
|
781
|
+
if (unlockDevice.length > 0)
|
|
782
|
+
view.ensureUnlockHost().openForDevice(unlockDevice, view.currentPane.sidebar)
|
|
783
|
+
if (view.dualMode) view.focusPane(view.focusSide)
|
|
784
|
+
trashSweep.start()
|
|
785
|
+
UpdateCheck.startPolling()
|
|
786
|
+
// Last, behind the listing request, and still before ui/boot/shell.qml creates the window and reads this body's colour.
|
|
787
|
+
Glass.launch()
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// The 30 day sweep runs off the startup path, not on it: a Trash listing costs one gio call per
|
|
791
|
+
// item and first paint is measured. Late enough that the window is up and the backend is
|
|
792
|
+
// answering, long before anyone reaches the Trash rail row. ui/TrashHost.qml refuses it when the
|
|
793
|
+
// setting is off, when it has already run today, and when the Trash window exists at all.
|
|
794
|
+
Timer {
|
|
795
|
+
id: trashSweep
|
|
796
|
+
interval: 2000
|
|
797
|
+
repeat: false
|
|
798
|
+
onTriggered: primaryPane.trash.sweep()
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// The test seam (AGENTS.md "Testing"), built once the first listing answers or waits, never for the first frame.
|
|
802
|
+
Loader {
|
|
803
|
+
id: ipcLoader
|
|
804
|
+
active: false
|
|
805
|
+
asynchronous: true
|
|
806
|
+
Component.onCompleted: setSource("Ipc.qml", {
|
|
807
|
+
fleaWindow: Qt.binding(function () { return view.host }),
|
|
808
|
+
pane: Qt.binding(function () { return view.currentPane }),
|
|
809
|
+
panes: Qt.binding(function () { return [primaryPane, secondPane.item ? secondPane.item.pane : null] }),
|
|
810
|
+
bar: Qt.binding(function () { return bar }),
|
|
811
|
+
backend: Qt.binding(function () { return view.currentPane.backend }),
|
|
812
|
+
chrome: Qt.binding(function () { return chrome }),
|
|
813
|
+
tabBar: Qt.binding(function () { return tabBar }),
|
|
814
|
+
convertDialog: Qt.binding(function () { return convertDialog.item }),
|
|
815
|
+
permissionsDialog: Qt.binding(function () { return permissionsDialog.item }),
|
|
816
|
+
bulkRenameDialog: Qt.binding(function () { return view.bulkRenameDialog }),
|
|
817
|
+
keymapSheet: Qt.binding(function () { return keymapSheet.item }),
|
|
818
|
+
settingsPanel: Qt.binding(function () { return settingsPanel.item }),
|
|
819
|
+
networkDialog: Qt.binding(function () { return networkDialog.item }),
|
|
820
|
+
shareBrowser: Qt.binding(function () { return shareLoader.item }),
|
|
821
|
+
emptyState: Qt.binding(function () { return view.currentPane.emptyState }),
|
|
822
|
+
archiveCards: Qt.binding(function () { return view.archiveCards }),
|
|
823
|
+
unlockHost: Qt.binding(function () { return view.unlockHost })
|
|
824
|
+
})
|
|
825
|
+
}
|
|
826
|
+
readonly property bool clearSurface: Glass.clear
|
|
827
|
+
}
|