flea-qml 0.0.0-stage → 0.2.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 +511 -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 +407 -0
- package/src/archiveops.js +728 -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 +135 -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/terminal-server.js +95 -0
- package/src/terminal-shell.js +26 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +327 -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 +307 -0
- package/test/file-url.test.js +24 -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-archive-here-ui.mjs +47 -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-folder-focus.mjs +63 -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-terminal-lifecycle.mjs +42 -0
- package/test/manual-terminal-navigation.mjs +48 -0
- package/test/manual-terminal-ui.mjs +47 -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 +95 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchiveHereConfirm.qml +127 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +775 -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 +525 -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 +95 -0
- package/ui/Pane.qml +1320 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +268 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +601 -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 +1005 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +91 -0
- package/ui/PreviewColumn.qml +553 -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 +41 -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 +728 -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/TerminalService.qml +69 -0
- package/ui/TerminalTab.qml +48 -0
- package/ui/TerminalTabs.qml +27 -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 +829 -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 +131 -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 +360 -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 +504 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +314 -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 +751 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +180 -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 +84 -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 +230 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import qs.Commons
|
|
3
|
+
import "." as Flea
|
|
4
|
+
import "js/Ops.js" as Ops
|
|
5
|
+
import "js/Transfer.js" as Transfer
|
|
6
|
+
|
|
7
|
+
// The released transfer card remains the pointer cancellation surface above the informational footer.
|
|
8
|
+
Item {
|
|
9
|
+
id: root
|
|
10
|
+
|
|
11
|
+
// ui/js/Ops.js's transfer, reassigned on every wire line; running false is what hides the card.
|
|
12
|
+
property var transfer: Ops.emptyTransfer()
|
|
13
|
+
property var owner: null
|
|
14
|
+
readonly property var cancelItem: cancelButton
|
|
15
|
+
readonly property alias byteText: byteLine.text
|
|
16
|
+
readonly property alias headlineText: headline.text
|
|
17
|
+
signal cancelRequested(int id)
|
|
18
|
+
|
|
19
|
+
// Everything drawn comes off this sample rather than straight off the wire, because thirty
|
|
20
|
+
// thousand small files change the name faster than it can be read. Idle rather than bound to
|
|
21
|
+
// transfer: a binding would track the wire live until the first tick happened to break it.
|
|
22
|
+
property var shown: Ops.emptyTransfer()
|
|
23
|
+
// About four changes a second, which is what the eye reads. The backend's own byte heartbeat is
|
|
24
|
+
// 150 ms (src/backend/opsreq.rs PROGRESS_EVERY), so a large file loses almost nothing here.
|
|
25
|
+
readonly property int publishMs: 250
|
|
26
|
+
// TransferCard rule 3: the rate is what this card published over the last two seconds, so one
|
|
27
|
+
// slow beat cannot make it jump and a stall reads 0 B/s rather than a stale number.
|
|
28
|
+
readonly property int rateWindowMs: 2000
|
|
29
|
+
property var rateSamples: []
|
|
30
|
+
readonly property real rate: {
|
|
31
|
+
if (root.rateSamples.length < 2)
|
|
32
|
+
return 0
|
|
33
|
+
var first = root.rateSamples[0]
|
|
34
|
+
var last = root.rateSamples[root.rateSamples.length - 1]
|
|
35
|
+
var span = (last.at - first.at) / 1000
|
|
36
|
+
return span > 0 ? Math.max(0, (last.bytes - first.bytes) / span) : 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Set the instant Cancel is pressed. src/backend/copyfile.rs stops the item in flight and
|
|
40
|
+
// removes what it wrote, so this covers only the round trip to transferdone and the beat above.
|
|
41
|
+
property bool cancelling: false
|
|
42
|
+
|
|
43
|
+
// The bar's thickness comes off the type scale the way a mark does, so it follows the display
|
|
44
|
+
// text size instead of pinning a pixel.
|
|
45
|
+
readonly property int barHeight: Math.round(Theme.font.caption / 2)
|
|
46
|
+
readonly property real trackOpacity: 0.25
|
|
47
|
+
// The one other popup in this design, ui/ConvertDialog.qml, is 300 design pixels wide; a second
|
|
48
|
+
// popup at a second width would be two languages.
|
|
49
|
+
readonly property int cardWidth: 300
|
|
50
|
+
|
|
51
|
+
visible: root.shown.running
|
|
52
|
+
implicitWidth: Theme.space(root.cardWidth)
|
|
53
|
+
implicitHeight: body.implicitHeight + 2 * Theme.spacing.rowPaddingX
|
|
54
|
+
width: root.implicitWidth
|
|
55
|
+
height: root.implicitHeight
|
|
56
|
+
|
|
57
|
+
onTransferChanged: {
|
|
58
|
+
// The first sample and the last are published at once; the ones between wait for the beat.
|
|
59
|
+
if (!root.transfer.running || !root.shown.running || root.transfer.id !== root.shown.id) {
|
|
60
|
+
root.publish()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
onOwnerChanged: root.publish()
|
|
64
|
+
|
|
65
|
+
Timer {
|
|
66
|
+
interval: root.publishMs
|
|
67
|
+
repeat: true
|
|
68
|
+
running: root.transfer.running
|
|
69
|
+
onTriggered: root.publish()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// One published sample, and the window the rate is measured over: everything older than two
|
|
73
|
+
// seconds goes except the one sample just past the edge, which is what the span is taken from.
|
|
74
|
+
function publish() {
|
|
75
|
+
var fresh = root.transfer.id !== root.shown.id ? [] : root.rateSamples
|
|
76
|
+
root.shown = root.transfer
|
|
77
|
+
var now = Date.now()
|
|
78
|
+
var next = fresh.concat([{at: now, bytes: Transfer.movedBytes(root.shown)}])
|
|
79
|
+
var keep = 0
|
|
80
|
+
while (keep + 1 < next.length && now - next[keep + 1].at > root.rateWindowMs) {
|
|
81
|
+
keep += 1
|
|
82
|
+
}
|
|
83
|
+
root.rateSamples = next.slice(keep)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Dimmed while the drive is being flushed: every file is already complete, so there
|
|
87
|
+
// is nothing left to stop.
|
|
88
|
+
function cancel() {
|
|
89
|
+
if (!Transfer.cancelEnabled(root.transfer) || root.cancelling) return
|
|
90
|
+
root.cancelRequested(root.transfer.id)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
MouseArea {
|
|
94
|
+
anchors.fill: parent
|
|
95
|
+
hoverEnabled: true
|
|
96
|
+
acceptedButtons: Qt.AllButtons
|
|
97
|
+
onWheel: function(wheel) { wheel.accepted = true }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Rectangle {
|
|
101
|
+
anchors.fill: parent
|
|
102
|
+
color: Theme.color.surface
|
|
103
|
+
border.width: Theme.spacing.hairline
|
|
104
|
+
border.color: Theme.color.mutedFrame
|
|
105
|
+
// Mirrors hyprland decoration:rounding, the same as the menu and the convert popup; 0 on a
|
|
106
|
+
// stock box, and the bar inside stays square either way.
|
|
107
|
+
radius: Style.cornerRadius
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Column {
|
|
111
|
+
id: body
|
|
112
|
+
x: Theme.spacing.rowPaddingX
|
|
113
|
+
y: Theme.spacing.rowPaddingX
|
|
114
|
+
width: root.width - 2 * Theme.spacing.rowPaddingX
|
|
115
|
+
spacing: Theme.spacing.gap
|
|
116
|
+
|
|
117
|
+
Item {
|
|
118
|
+
width: parent.width
|
|
119
|
+
height: headline.implicitHeight
|
|
120
|
+
|
|
121
|
+
// The card is chrome, so its mark is the OEM icon token the chrome bar's own marks
|
|
122
|
+
// take; the status bar's spiral is caption-sized because the bar's own text is.
|
|
123
|
+
Flea.Spinner {
|
|
124
|
+
id: crawl
|
|
125
|
+
anchors.left: parent.left
|
|
126
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
127
|
+
width: Theme.chromeMarkSize
|
|
128
|
+
height: Theme.chromeMarkSize
|
|
129
|
+
color: Theme.color.mutedFrame
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Text {
|
|
133
|
+
id: headline
|
|
134
|
+
anchors.left: crawl.right
|
|
135
|
+
anchors.leftMargin: Theme.spacing.gap
|
|
136
|
+
anchors.right: parent.right
|
|
137
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
138
|
+
text: Transfer.head(root.shown)
|
|
139
|
+
color: Theme.color.foreground
|
|
140
|
+
font.family: Theme.font.family
|
|
141
|
+
font.pixelSize: Theme.font.body
|
|
142
|
+
textFormat: Text.PlainText
|
|
143
|
+
elide: Text.ElideRight
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Text {
|
|
148
|
+
width: parent.width
|
|
149
|
+
visible: text.length > 0
|
|
150
|
+
text: Transfer.fileLine(root.shown)
|
|
151
|
+
color: Theme.color.muted
|
|
152
|
+
font.family: Theme.font.family
|
|
153
|
+
font.pixelSize: Theme.font.caption
|
|
154
|
+
textFormat: Text.PlainText
|
|
155
|
+
// The middle goes and the extension stays: the extension is what says what the file is.
|
|
156
|
+
elide: Text.ElideMiddle
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Square ends, track and fill both: no row or bar in this language rounds. The fill is a
|
|
160
|
+
// sibling of the track and not its child, because opacity multiplies down into children.
|
|
161
|
+
Item {
|
|
162
|
+
width: parent.width
|
|
163
|
+
height: root.barHeight
|
|
164
|
+
|
|
165
|
+
Rectangle {
|
|
166
|
+
anchors.fill: parent
|
|
167
|
+
color: Theme.color.mutedFrame
|
|
168
|
+
opacity: root.trackOpacity
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Rectangle {
|
|
172
|
+
width: parent.width * Transfer.fraction(root.shown)
|
|
173
|
+
height: parent.height
|
|
174
|
+
color: Theme.color.accentFrame
|
|
175
|
+
|
|
176
|
+
// The card publishes four times a second; easing across the gap is what makes the
|
|
177
|
+
// fill read as movement rather than as four steps a second.
|
|
178
|
+
Behavior on width {
|
|
179
|
+
enabled: !Theme.reducedMotion
|
|
180
|
+
NumberAnimation { duration: root.publishMs; easing.type: Easing.Linear }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// TransferCard rule 1: the bar's own caption, the figures the operator is waiting on inked
|
|
186
|
+
// in the foreground and the words between them muted. Rule 4 makes it absent, not blank,
|
|
187
|
+
// until the first byte sample lands, so the card is one row shorter until then.
|
|
188
|
+
Row {
|
|
189
|
+
id: byteLine
|
|
190
|
+
readonly property var parts: Transfer.byteParts(root.shown, root.rate)
|
|
191
|
+
// The drawn line as one string, so a driven case reads what the card says rather than a shot.
|
|
192
|
+
readonly property string text: byteLine.parts.map(function (p) { return p.text }).join("")
|
|
193
|
+
visible: byteLine.parts.length > 0
|
|
194
|
+
height: visible ? implicitHeight : 0
|
|
195
|
+
|
|
196
|
+
Repeater {
|
|
197
|
+
model: byteLine.parts
|
|
198
|
+
|
|
199
|
+
delegate: Text {
|
|
200
|
+
required property var modelData
|
|
201
|
+
text: modelData.text
|
|
202
|
+
color: modelData.figure ? Theme.color.foreground : Theme.color.muted
|
|
203
|
+
font.family: Theme.font.family
|
|
204
|
+
font.pixelSize: Theme.font.caption
|
|
205
|
+
textFormat: Text.PlainText
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
Item {
|
|
211
|
+
width: parent.width
|
|
212
|
+
height: cancelButton.implicitHeight
|
|
213
|
+
|
|
214
|
+
// Variant A: the card's Cancel is the one control every dialog draws.
|
|
215
|
+
Flea.DialogButton {
|
|
216
|
+
id: cancelButton
|
|
217
|
+
anchors.right: parent.right
|
|
218
|
+
visible: !root.cancelling
|
|
219
|
+
label: "Cancel"
|
|
220
|
+
available: Transfer.cancelEnabled(root.shown)
|
|
221
|
+
enabled: available
|
|
222
|
+
onActivated: root.cancel()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// The cancel is in and the item in flight is being finished rather than torn in half,
|
|
226
|
+
// which is a state the operator has to be able to see rather than infer from a dead button.
|
|
227
|
+
Text {
|
|
228
|
+
visible: root.cancelling
|
|
229
|
+
anchors.right: parent.right
|
|
230
|
+
anchors.verticalCenter: parent.verticalCenter
|
|
231
|
+
text: "Cancelling"
|
|
232
|
+
color: Theme.color.muted
|
|
233
|
+
font.family: Theme.font.family
|
|
234
|
+
font.pixelSize: Theme.font.body
|
|
235
|
+
textFormat: Text.PlainText
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import qs.Commons
|
|
3
|
+
import "." as Flea
|
|
4
|
+
import "js/Trash.js" as Trash
|
|
5
|
+
|
|
6
|
+
// The destructive choice must be reached deliberately; a reflexive Enter activates Cancel.
|
|
7
|
+
FocusScope {
|
|
8
|
+
id: root
|
|
9
|
+
anchors.fill: parent
|
|
10
|
+
visible: opened || opacity > 0
|
|
11
|
+
// Closed means no input, even while the fade-out is still drawn.
|
|
12
|
+
enabled: opened
|
|
13
|
+
opacity: 0
|
|
14
|
+
property bool opened: false
|
|
15
|
+
property var snapshot: ({})
|
|
16
|
+
property bool destructiveFocus: false
|
|
17
|
+
property string scopeName: "Trash items"
|
|
18
|
+
readonly property real referenceScale: Theme.font.bodySmall / 13
|
|
19
|
+
readonly property real cardPadding: Math.round(16 * referenceScale)
|
|
20
|
+
readonly property real cardBottomPadding: Theme.spacing.rowPaddingX
|
|
21
|
+
signal confirmed(int token)
|
|
22
|
+
signal cancelled()
|
|
23
|
+
readonly property var cardItem: card
|
|
24
|
+
readonly property var cancelItem: cancelButton
|
|
25
|
+
readonly property var dangerItem: dangerButton
|
|
26
|
+
readonly property string titleText: title.text
|
|
27
|
+
readonly property string bodyText: bodyLabel.text
|
|
28
|
+
function open(value) { snapshot = value; destructiveFocus = false; body.contentY = 0; opened = true; forceActiveFocus() }
|
|
29
|
+
function close() { opened = false }
|
|
30
|
+
function cancel() { close(); cancelled() }
|
|
31
|
+
function activate() {
|
|
32
|
+
if (!destructiveFocus) { cancel(); return }
|
|
33
|
+
var token = snapshot.token
|
|
34
|
+
close()
|
|
35
|
+
confirmed(token)
|
|
36
|
+
}
|
|
37
|
+
Flea.Reveal { target: root; shown: root.opened; lift: card }
|
|
38
|
+
Keys.onPressed: function(event) {
|
|
39
|
+
if (event.key === Qt.Key_Escape) root.cancel()
|
|
40
|
+
else if (event.modifiers & (Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)) { event.accepted = true; return }
|
|
41
|
+
else if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) root.destructiveFocus = !root.destructiveFocus
|
|
42
|
+
else if (event.key === Qt.Key_L || event.key === Qt.Key_Right) root.destructiveFocus = true
|
|
43
|
+
else if (event.key === Qt.Key_H || event.key === Qt.Key_Left) root.destructiveFocus = false
|
|
44
|
+
else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Space) root.activate()
|
|
45
|
+
if (root.opened) body.reveal(root.destructiveFocus ? dangerButton : cancelButton)
|
|
46
|
+
event.accepted = true
|
|
47
|
+
}
|
|
48
|
+
Rectangle {
|
|
49
|
+
anchors.fill: parent
|
|
50
|
+
color: Theme.color.background
|
|
51
|
+
opacity: 0.5
|
|
52
|
+
MouseArea {
|
|
53
|
+
anchors.fill: parent
|
|
54
|
+
hoverEnabled: true
|
|
55
|
+
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
56
|
+
onClicked: root.cancel()
|
|
57
|
+
onWheel: function(wheel) { wheel.accepted = true }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
Rectangle {
|
|
61
|
+
id: card
|
|
62
|
+
x: Theme.cardOrigin(root.width, width)
|
|
63
|
+
y: Theme.cardOrigin(root.height, height)
|
|
64
|
+
// TrashSidebar's 340px content width excludes its two 16px paddings and hairlines.
|
|
65
|
+
width: Theme.cardSpan(Math.round(340 * root.referenceScale * Theme.dialogWidthRatio) + 2 * root.cardPadding + 2 * Theme.spacing.hairline, root.width - 2 * Theme.spacing.gap)
|
|
66
|
+
height: Theme.cardSpan(body.wanted + root.cardPadding + root.cardBottomPadding + 2 * Theme.spacing.hairline, root.height - 2 * Theme.spacing.gap)
|
|
67
|
+
color: Theme.color.surface
|
|
68
|
+
border.color: Theme.color.mutedFrame
|
|
69
|
+
border.width: Theme.spacing.hairline
|
|
70
|
+
radius: Style.cornerRadius
|
|
71
|
+
MouseArea {
|
|
72
|
+
anchors.fill: parent
|
|
73
|
+
hoverEnabled: true
|
|
74
|
+
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
75
|
+
onWheel: function(wheel) { wheel.accepted = true }
|
|
76
|
+
}
|
|
77
|
+
Flea.CardFrame {
|
|
78
|
+
id: body
|
|
79
|
+
bleed: Theme.focusClearance
|
|
80
|
+
anchors.fill: parent
|
|
81
|
+
anchors.leftMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
|
|
82
|
+
anchors.rightMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
|
|
83
|
+
anchors.topMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
|
|
84
|
+
anchors.bottomMargin: root.cardBottomPadding + Theme.spacing.hairline - Theme.focusClearance
|
|
85
|
+
title: Row {
|
|
86
|
+
width: parent.width
|
|
87
|
+
spacing: Theme.spacing.gap
|
|
88
|
+
Flea.Glyph { id: alertMark; width: Theme.font.bodySmall * 1.3; height: title.height; name: "alert"; color: Theme.color.error }
|
|
89
|
+
Text {
|
|
90
|
+
id: title
|
|
91
|
+
width: parent.width - alertMark.width - parent.spacing
|
|
92
|
+
text: Trash.confirmTitle(root.snapshot)
|
|
93
|
+
textFormat: Text.PlainText
|
|
94
|
+
wrapMode: Text.Wrap
|
|
95
|
+
color: Theme.color.foreground
|
|
96
|
+
font { family: Theme.font.family; pixelSize: Theme.font.body; bold: true }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
buttons: Flow {
|
|
100
|
+
width: Math.min(parent.width, cancelButton.implicitWidth + dangerButton.implicitWidth + spacing)
|
|
101
|
+
anchors.right: parent.right
|
|
102
|
+
spacing: Theme.spacing.gap
|
|
103
|
+
Flea.DialogButton {
|
|
104
|
+
id: cancelButton
|
|
105
|
+
label: "Cancel"
|
|
106
|
+
primary: true
|
|
107
|
+
focused: !root.destructiveFocus
|
|
108
|
+
onActivated: root.cancel()
|
|
109
|
+
}
|
|
110
|
+
Flea.DialogButton {
|
|
111
|
+
id: dangerButton
|
|
112
|
+
label: root.snapshot.all ? "Empty Trash" : "Delete"
|
|
113
|
+
destructive: true
|
|
114
|
+
focused: root.destructiveFocus
|
|
115
|
+
onActivated: { root.destructiveFocus = true; root.activate() }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
Text {
|
|
119
|
+
id: bodyLabel
|
|
120
|
+
width: parent.width
|
|
121
|
+
text: Trash.confirmBody(root.snapshot, root.scopeName)
|
|
122
|
+
textFormat: Text.PlainText
|
|
123
|
+
wrapMode: Text.Wrap
|
|
124
|
+
color: Theme.color.foreground
|
|
125
|
+
font { family: Theme.font.family; pixelSize: Theme.font.body }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "js/TrashDrop.js" as TrashDrop
|
|
3
|
+
|
|
4
|
+
// The rail loads this bridge on its first matching drag, keeping TrashDrop.js off the first frame.
|
|
5
|
+
FileDrag {
|
|
6
|
+
id: root
|
|
7
|
+
|
|
8
|
+
function accepts(marker, urls, path) { return TrashDrop.accepts(marker, urls, path, root.pane ? root.pane.backend : null) }
|
|
9
|
+
function line(marker, urls, path) { return TrashDrop.line(marker, urls, path, root.pane ? root.pane.backend : null) }
|
|
10
|
+
function drop(pane, marker, urls) { return TrashDrop.drop(pane, marker, urls) }
|
|
11
|
+
}
|
package/ui/TrashHost.qml
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
import "js/Focus.js" as Focus
|
|
4
|
+
import "js/Menu.js" as Menu
|
|
5
|
+
import "js/Ops.js" as Ops
|
|
6
|
+
import "js/RecentMode.js" as RecentMode
|
|
7
|
+
import "js/TrashDates.js" as TrashDates
|
|
8
|
+
|
|
9
|
+
// Only Trash-specific operations live here. Pane owns the visible listing,
|
|
10
|
+
// keyboard, filtering, thumbnails and Quick Look, just as for any other location.
|
|
11
|
+
Item {
|
|
12
|
+
id: root
|
|
13
|
+
required property var pane
|
|
14
|
+
property Item overlayParent: null
|
|
15
|
+
readonly property bool browsing: pane.recentMode === "trash"
|
|
16
|
+
// Compatibility with callers checking for the old full-pane overlay.
|
|
17
|
+
readonly property bool opened: false
|
|
18
|
+
readonly property bool confirming: (confirmation !== null && confirmation.opened) || pendingReview !== null || pendingOp === "prepare"
|
|
19
|
+
readonly property var item: root
|
|
20
|
+
readonly property var sheetPane: pane
|
|
21
|
+
readonly property int total: browsing ? pane.total : 0
|
|
22
|
+
readonly property int selectedCount: browsing ? pane.selectionCount() : 0
|
|
23
|
+
readonly property bool active: browsing
|
|
24
|
+
property int requestId: 1000000
|
|
25
|
+
property string pendingOp: ""
|
|
26
|
+
property bool pendingAll: false
|
|
27
|
+
property int pendingToken: 0
|
|
28
|
+
property var pendingReview: null
|
|
29
|
+
property var pendingRows: []
|
|
30
|
+
property int pendingListing: 0
|
|
31
|
+
readonly property bool busy: pendingOp.length > 0
|
|
32
|
+
readonly property var confirmationItem: confirmation
|
|
33
|
+
readonly property bool confirmationOpen: confirmation !== null && confirmation.opened
|
|
34
|
+
// The 30 day sweep, decided 2026-09-11. It runs the same three requests the window runs,
|
|
35
|
+
// in the same order, so prepare still reviews every item against the identity the listing
|
|
36
|
+
// reported and delete still refuses a token the trash has moved under. This host drives it
|
|
37
|
+
// because the backend keeps ONE Trash session: two clients listing into it would take each
|
|
38
|
+
// other's snapshot away, so the window and the sweep are never in flight together.
|
|
39
|
+
readonly property int sweepDays: 30
|
|
40
|
+
// One window of the listing, which is also the most the backend will answer with. A Trash holding
|
|
41
|
+
// more than this is swept over as many days as it takes, which is slower and never wrong.
|
|
42
|
+
readonly property int sweepWindow: 350
|
|
43
|
+
property int sweepId: 0
|
|
44
|
+
property string sweepStage: ""
|
|
45
|
+
property var sweepDoomed: []
|
|
46
|
+
readonly property bool sweeping: root.sweepStage.length > 0
|
|
47
|
+
|
|
48
|
+
function sweep() {
|
|
49
|
+
if (root.sweeping || root.browsing || !ViewState.trashAutoEmpty)
|
|
50
|
+
return
|
|
51
|
+
if (TrashDates.dayNumber(Date.now()) === ViewState.trashSweptOn)
|
|
52
|
+
return
|
|
53
|
+
root.sweepSend("list", {start: 0, count: root.sweepWindow, recover: false})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sweepSend(op, fields) {
|
|
57
|
+
var message = fields || {}
|
|
58
|
+
message.c = "trashbrowse"
|
|
59
|
+
message.op = op
|
|
60
|
+
message.id = ++root.sweepId
|
|
61
|
+
root.sweepStage = op
|
|
62
|
+
root.pane.backend.send(message)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// The operator reaching for Trash outranks a sweep nobody asked to watch. Cancelling drops the
|
|
66
|
+
// prepared confirmation in the backend, so the window's own list starts from a clean session.
|
|
67
|
+
function sweepStop() {
|
|
68
|
+
if (!root.sweeping)
|
|
69
|
+
return
|
|
70
|
+
root.sweepStage = ""
|
|
71
|
+
root.sweepDoomed = []
|
|
72
|
+
root.pane.backend.send({c: "trashbrowse", op: "cancel", id: ++root.sweepId, clearSelection: true})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Every reply while a sweep is in flight is the sweep's, because the window cannot be open then.
|
|
76
|
+
// A failure at any stage ends the sweep and records nothing, so the next launch tries again.
|
|
77
|
+
function sweepReceive(message) {
|
|
78
|
+
if (message.ok !== true) { root.sweepStage = ""; root.sweepDoomed = []; return }
|
|
79
|
+
if (message.op === "list") {
|
|
80
|
+
var rows = message.rows || []
|
|
81
|
+
var now = Date.now()
|
|
82
|
+
var doomed = []
|
|
83
|
+
for (var i = 0; i < rows.length; i++) {
|
|
84
|
+
if (TrashDates.expired(rows[i].deleted, now, root.sweepDays))
|
|
85
|
+
doomed.push(rows[i])
|
|
86
|
+
}
|
|
87
|
+
if (doomed.length === 0) { root.sweepFinished(); return }
|
|
88
|
+
root.sweepDoomed = doomed
|
|
89
|
+
root.sweepSend("prepare", {all: false, emptyTrash: false,
|
|
90
|
+
uris: doomed.map(function (row) { return row.uri }),
|
|
91
|
+
identities: doomed.map(function (row) { return row.identity })})
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if (message.op === "prepare") {
|
|
95
|
+
// A prepare that reviewed nothing has nothing to delete, and asking anyway would spend a
|
|
96
|
+
// token on an empty confirmation.
|
|
97
|
+
if (!message.count) { root.sweepFinished(); return }
|
|
98
|
+
root.sweepSend("delete", {token: message.token})
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
if (message.op === "delete") {
|
|
102
|
+
// The rail is hidden, so its TrashMonitor is unloaded with it: nothing to refresh.
|
|
103
|
+
if (root.pane.sidebar) root.pane.sidebar.refreshTrash()
|
|
104
|
+
root.sweepFinished()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function sweepFinished() {
|
|
109
|
+
root.sweepStage = ""
|
|
110
|
+
root.sweepDoomed = []
|
|
111
|
+
ViewState.recordTrashSweep(TrashDates.dayNumber(Date.now()))
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
function open(action) {
|
|
116
|
+
if (confirming || busy) return
|
|
117
|
+
sweepStop()
|
|
118
|
+
if (!browsing) {
|
|
119
|
+
if (pane.preview) pane.preview.close()
|
|
120
|
+
pane.shareBrowser.close()
|
|
121
|
+
pane.clearSelection()
|
|
122
|
+
pane.focusView = Focus.LIST
|
|
123
|
+
RecentMode.run(pane, [], {}, "trash")
|
|
124
|
+
}
|
|
125
|
+
if (action === "emptyTrash" || action === "restoreAll") root.action(action)
|
|
126
|
+
else pane.listArea.forceActiveFocus()
|
|
127
|
+
}
|
|
128
|
+
// Navigation already owns the next listing; do not issue a second list here.
|
|
129
|
+
function close() { if (browsing && !confirming) RecentMode.leave(pane) }
|
|
130
|
+
function send(op, fields) {
|
|
131
|
+
pendingOp = op
|
|
132
|
+
pane.backend.send(Object.assign({c:"trashbrowse",id:++requestId,op:op},fields || {}))
|
|
133
|
+
}
|
|
134
|
+
function action(name, menuId) {
|
|
135
|
+
if (busy || confirming) return
|
|
136
|
+
if (name === "openTrash") { open(); return }
|
|
137
|
+
var all = name === "emptyTrash" || name === "restoreAll"
|
|
138
|
+
if (!all && !browsing) return
|
|
139
|
+
if (!all && pane.listInFlight) { pane.message("A directory is already loading.", false); return }
|
|
140
|
+
pendingAll = all
|
|
141
|
+
pendingRows = all ? [] : Ops.targetIndices(pane)
|
|
142
|
+
pendingListing = pane.backend.heldListing
|
|
143
|
+
if (!all && !menuId && pendingRows.length === 0) return
|
|
144
|
+
var fields = all ? {all:true,emptyTrash:name === "emptyTrash"}
|
|
145
|
+
: {all:false,rows:pendingRows,listing:pendingListing}
|
|
146
|
+
if (menuId && !all) fields.menuId = menuId
|
|
147
|
+
send(name === "restoreAll" || name === "restoreTrashSelection" ? "restore" : "prepare", fields)
|
|
148
|
+
}
|
|
149
|
+
function refresh() { if (browsing && !pane.listInFlight) RecentMode.refresh(pane, "") }
|
|
150
|
+
function menuEntries(selection, hidden) {
|
|
151
|
+
return selection ? Menu.applyHidden([
|
|
152
|
+
{label:"Restore",action:"restoreTrashSelection",glyph:"undo",disabled:busy},
|
|
153
|
+
{separator:true},
|
|
154
|
+
{label:"Delete permanently",action:"deletePermanently",glyph:"trash",danger:true,disabled:busy}
|
|
155
|
+
],hidden) : Menu.trashEntries(total,busy)
|
|
156
|
+
}
|
|
157
|
+
function sheetFocus() { pane.listArea.forceActiveFocus() }
|
|
158
|
+
function sheetActionRows(rows) { return rows }
|
|
159
|
+
function sheetMenuModel() { return menuEntries(true,[]).concat(menuEntries(false,[])) }
|
|
160
|
+
function sheetMenuAction(name) { action(name) }
|
|
161
|
+
function sheetAction(name) { Focus.dispatchAction(name,pane) }
|
|
162
|
+
readonly property var confirmation: confirmationLoader.item
|
|
163
|
+
Loader { id:confirmationLoader; active:false; source:"TrashConfirm.qml"; onLoaded:{item.parent=root.overlayParent || root;item.z=5;if(root.pendingReview){item.open(root.pendingReview);root.pendingReview=null}} }
|
|
164
|
+
Connections {
|
|
165
|
+
target:root.confirmation
|
|
166
|
+
function onConfirmed(token) { root.send("delete",{token:token}) }
|
|
167
|
+
function onCancelled() { root.send("cancel");root.pane.listArea.forceActiveFocus() }
|
|
168
|
+
}
|
|
169
|
+
Connections {
|
|
170
|
+
target: root.pane.backend
|
|
171
|
+
function onTrashResult(message) {
|
|
172
|
+
if (root.sweeping) { root.sweepReceive(message); return }
|
|
173
|
+
if (message.id !== root.requestId || message.op !== root.pendingOp) return
|
|
174
|
+
root.pendingOp = ""
|
|
175
|
+
if (!message.ok) {
|
|
176
|
+
if (confirmation) confirmation.close()
|
|
177
|
+
root.pane.message(message.error || "Trash operation failed",true)
|
|
178
|
+
root.pane.listArea.forceActiveFocus()
|
|
179
|
+
root.refresh()
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
if (message.op === "prepare") { root.pendingToken=message.token; if(confirmation)confirmation.open(message);else{root.pendingReview=message;confirmationLoader.active=true;} return }
|
|
183
|
+
if (message.op === "check" && !message.valid) { if (confirmation) confirmation.close(); root.pane.message("The Trash changed; review it again.",true) }
|
|
184
|
+
if (message.op === "restore" || message.op === "delete") {
|
|
185
|
+
root.pane.operationResult((message.op === "restore" ? "Restored " : "Deleted ")+message.done+" items",
|
|
186
|
+
(message.failures || []).map(function(f){return f.error}).join("\n"),message.failed > 0)
|
|
187
|
+
if(root.pane.sidebar)root.pane.sidebar.refreshTrash()
|
|
188
|
+
root.refresh()
|
|
189
|
+
}
|
|
190
|
+
if (message.op !== "check") root.pane.listArea.forceActiveFocus()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
Timer { interval:2500; repeat:true; running:root.confirmationOpen && !root.busy; onTriggered:root.send("check",{token:root.pendingToken}) }
|
|
194
|
+
Connections {
|
|
195
|
+
target: root.pane.sidebar
|
|
196
|
+
function onTrashChanged() { if(root.browsing && !root.busy && !root.confirming)root.refresh() }
|
|
197
|
+
}
|
|
198
|
+
Connections {
|
|
199
|
+
target: root.pane.contextMenu()
|
|
200
|
+
function onRailChosen(action,key) { if(key === "trash")root.action(action) }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
|
|
3
|
+
// flea-qml: the shared Trash count, as the pane's backend reports it with a `trashcount` line at
|
|
4
|
+
// start and on every change. The backend reads the FreeDesktop.org Trash that src/trash.js writes;
|
|
5
|
+
// upstream ran `gio monitor` and `gio info` under setpriv and sh here. Nothing extra runs: the
|
|
6
|
+
// backend already lives as long as the interface.
|
|
7
|
+
Item {
|
|
8
|
+
id: root
|
|
9
|
+
// Not named enabled, which would shadow Item's own input switch.
|
|
10
|
+
property bool watching: true
|
|
11
|
+
property var backend: null
|
|
12
|
+
readonly property int reported: root.backend ? root.backend.trashCount : -1
|
|
13
|
+
readonly property int revision: root.backend ? root.backend.trashRevision : 0
|
|
14
|
+
property int count: 0
|
|
15
|
+
property bool ready: false
|
|
16
|
+
signal changed()
|
|
17
|
+
signal failed(string message)
|
|
18
|
+
|
|
19
|
+
// The backend reports every change itself, so there is nothing to ask for again.
|
|
20
|
+
function refresh() {}
|
|
21
|
+
|
|
22
|
+
onRevisionChanged: if (root.watching && root.ready) root.changed()
|
|
23
|
+
onReportedChanged: {
|
|
24
|
+
if (!root.watching || root.reported < 0) return
|
|
25
|
+
root.count = root.reported
|
|
26
|
+
root.ready = true
|
|
27
|
+
}
|
|
28
|
+
onWatchingChanged: if (root.watching && root.reported >= 0) { root.count = root.reported; root.ready = true }
|
|
29
|
+
Component.onCompleted: if (root.reported >= 0) { root.count = root.reported; root.ready = true }
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import qs.Commons
|
|
3
|
+
Item {
|
|
4
|
+
id: root
|
|
5
|
+
property var trash: null
|
|
6
|
+
Repeater {
|
|
7
|
+
model: [{label:"Restore",action:"restoreTrashSelection"},{label:"Restore all",action:"restoreAll"},{label:"Empty Trash",action:"emptyTrash"}]
|
|
8
|
+
delegate: Rectangle {
|
|
9
|
+
required property var modelData
|
|
10
|
+
required property int index
|
|
11
|
+
x: index * (width + Theme.spacing.gap)
|
|
12
|
+
height: root.height
|
|
13
|
+
width: Math.min(Math.max(110, Theme.font.body * 8), (root.width - 2*Theme.spacing.gap)/3)
|
|
14
|
+
color: Theme.color.surface
|
|
15
|
+
Text { anchors.centerIn:parent; text:modelData.label; color:Theme.color.foreground; font {family:Theme.font.family;pixelSize:Theme.font.body} }
|
|
16
|
+
MouseArea { anchors.fill:parent; onClicked:if(root.trash)root.trash.action(modelData.action) }
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|