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,149 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import Quickshell
|
|
3
|
+
import Quickshell.Io
|
|
4
|
+
import "js/FleaBin.js" as FleaBin
|
|
5
|
+
|
|
6
|
+
// Listings can block inside FUSE. Only this read-only worker is replaceable; selection identities
|
|
7
|
+
// belong to the picker's separate backend, and the file manager's write backend is never signalled.
|
|
8
|
+
Item {
|
|
9
|
+
id: root
|
|
10
|
+
signal message(var value)
|
|
11
|
+
signal pathsResolved(var paths, int request)
|
|
12
|
+
signal failed(string reason)
|
|
13
|
+
signal quitReady()
|
|
14
|
+
property var current: null
|
|
15
|
+
property var pending: null
|
|
16
|
+
property bool quitting: false
|
|
17
|
+
|
|
18
|
+
function clear() {
|
|
19
|
+
pending = null
|
|
20
|
+
if (current) {
|
|
21
|
+
current.obsolete = true
|
|
22
|
+
if (current.running) current.signal(9)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function request(value) {
|
|
26
|
+
if (quitting) return
|
|
27
|
+
// Only list and listpaths have a worker to serve them, so any other request names itself here.
|
|
28
|
+
if (value.c !== "list" && value.c !== "listpaths") { console.warn("PickerListing refused request " + value.c); return }
|
|
29
|
+
pending = value
|
|
30
|
+
if (current) {
|
|
31
|
+
current.obsolete = true
|
|
32
|
+
if (current.running) current.signal(9)
|
|
33
|
+
} else launch()
|
|
34
|
+
}
|
|
35
|
+
function launch() {
|
|
36
|
+
if (quitting || !pending || current) return
|
|
37
|
+
var value = pending
|
|
38
|
+
pending = null
|
|
39
|
+
current = worker.createObject(root, {request: value})
|
|
40
|
+
current.running = true
|
|
41
|
+
}
|
|
42
|
+
function window(start, count) {
|
|
43
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
44
|
+
current.write(JSON.stringify({c: "window", start: start, count: count}) + "\n")
|
|
45
|
+
}
|
|
46
|
+
// Resolve names without asking metadata for rows outside the held window.
|
|
47
|
+
function paths(rows, request) {
|
|
48
|
+
if (!current || !current.running || current.obsolete || quitting) return false
|
|
49
|
+
current.pathRequests = current.pathRequests.concat([request])
|
|
50
|
+
current.write(JSON.stringify({c: "paths", rows: rows}) + "\n")
|
|
51
|
+
return true
|
|
52
|
+
}
|
|
53
|
+
// Storage class for the grid planner, asked after the listing lands.
|
|
54
|
+
function fsinfo() {
|
|
55
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
56
|
+
current.write(JSON.stringify({c: "fsinfo"}) + "\n")
|
|
57
|
+
}
|
|
58
|
+
// Sort reorders the worker's own listing without replacing it; the caller re-asks the window after.
|
|
59
|
+
function sort(by, desc, foldersFirst, groupByKind, hiddenLast) {
|
|
60
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
61
|
+
current.write(JSON.stringify({c: "sort", by: by, desc: desc, foldersFirst: foldersFirst, groupByKind: groupByKind, hiddenLast: hiddenLast}) + "\n")
|
|
62
|
+
}
|
|
63
|
+
// Thumbnails are planned against the rows this worker holds, so the ask goes to it and not to
|
|
64
|
+
// the picker's selection backend, which holds no listing at all. An empty ask names nothing.
|
|
65
|
+
function thumb(rows, cacheOnly) {
|
|
66
|
+
if (!rows || rows.length === 0) return
|
|
67
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
68
|
+
current.write(JSON.stringify({c: "thumb", rows: rows, cacheOnly: cacheOnly === true}) + "\n")
|
|
69
|
+
}
|
|
70
|
+
// An empty rows cancels everything queued, so an empty list is never sent; see docs/protocol.md.
|
|
71
|
+
function thumbcancel(rows) {
|
|
72
|
+
if (!rows || rows.length === 0) return
|
|
73
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
74
|
+
current.write(JSON.stringify({c: "thumbcancel", rows: rows}) + "\n")
|
|
75
|
+
}
|
|
76
|
+
// The path bar's folder jump, asked once per open of the field; the worker is the full backend, so it answers the main window's jumped line.
|
|
77
|
+
function jump(id, ranking, favourites, recent) {
|
|
78
|
+
if (current && current.running && !current.obsolete && !quitting) {
|
|
79
|
+
var message = {c: "jump", id: id, favourites: favourites, recent: recent}
|
|
80
|
+
if (ranking !== 0) message.ranking = ranking
|
|
81
|
+
current.write(JSON.stringify(message) + "\n")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Tab completion behind the picker's field: the directory the head names.
|
|
85
|
+
function peek(dir, first, hidden) {
|
|
86
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
87
|
+
current.write(JSON.stringify({c: "peek", path: dir, first: first, hidden: hidden}) + "\n")
|
|
88
|
+
}
|
|
89
|
+
// A typed file's row, named from the listing's own names: nothing is statted and no window is walked.
|
|
90
|
+
function locate(path) {
|
|
91
|
+
if (current && current.running && !current.obsolete && !quitting)
|
|
92
|
+
current.write(JSON.stringify({c: "locate", path: path}) + "\n")
|
|
93
|
+
}
|
|
94
|
+
function quit() {
|
|
95
|
+
quitting = true
|
|
96
|
+
pending = null
|
|
97
|
+
if (!current) { quitReady(); return }
|
|
98
|
+
current.obsolete = true
|
|
99
|
+
if (current.running) current.signal(9)
|
|
100
|
+
}
|
|
101
|
+
function ended(process, reason) {
|
|
102
|
+
if (current !== process) return
|
|
103
|
+
current = null
|
|
104
|
+
process.destroy()
|
|
105
|
+
if (quitting) quitReady()
|
|
106
|
+
else if (pending) Qt.callLater(launch)
|
|
107
|
+
else if (!process.obsolete) failed(reason)
|
|
108
|
+
}
|
|
109
|
+
Component.onDestruction: if (current && current.running) current.signal(9)
|
|
110
|
+
|
|
111
|
+
Component {
|
|
112
|
+
id: worker
|
|
113
|
+
Process {
|
|
114
|
+
id: process
|
|
115
|
+
property var request
|
|
116
|
+
property bool obsolete: false
|
|
117
|
+
property bool started: false
|
|
118
|
+
property var pathRequests: []
|
|
119
|
+
command: FleaBin.command(Quickshell.env("FLEA_BIN"), Quickshell.env("BUNINU_BUN"), Quickshell.shellDir, ["--backend"])
|
|
120
|
+
stdinEnabled: true
|
|
121
|
+
onStarted: {
|
|
122
|
+
started = true
|
|
123
|
+
if (obsolete || root.quitting) { signal(9); return }
|
|
124
|
+
write(JSON.stringify(request) + "\n")
|
|
125
|
+
}
|
|
126
|
+
stdout: SplitParser {
|
|
127
|
+
onRead: function(line) {
|
|
128
|
+
if (process.obsolete || root.quitting || root.current !== process || !line) return
|
|
129
|
+
// Sample reply: {"t":"paths","paths":["/folder/file.txt"]}; replies follow request order on this worker.
|
|
130
|
+
try {
|
|
131
|
+
var message = JSON.parse(line)
|
|
132
|
+
if (message.t === "paths") {
|
|
133
|
+
var request = process.pathRequests.shift()
|
|
134
|
+
if (request !== undefined) root.pathsResolved(message.paths, request)
|
|
135
|
+
} else root.message(message)
|
|
136
|
+
}
|
|
137
|
+
catch (error) { console.warn("PickerListing could not handle a worker reply: " + error); root.failed("This folder could not be listed.") }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
onExited: function(code, status) {
|
|
141
|
+
// The signal(9) above ends an obsolete or quitting worker on purpose, so only an unasked exit is worth a warning.
|
|
142
|
+
if (!process.obsolete && !root.quitting) console.warn("PickerListing worker exited with code " + code)
|
|
143
|
+
root.ended(process, "Flea stopped responding, so this folder could not be listed.")
|
|
144
|
+
}
|
|
145
|
+
// A failed QProcess spawn emits runningChanged, but never exited.
|
|
146
|
+
onRunningChanged: if (!running && !started) root.ended(process, "This folder could not be listed. Restart Flea.")
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "js/Format.js" as Format
|
|
3
|
+
import "js/PathBar.js" as PathBar
|
|
4
|
+
import "js/Picker.js" as Picker
|
|
5
|
+
|
|
6
|
+
// A committed picker line, named by the backend's peek first: a folder opens, a file answers through its row's accept, nothing else.
|
|
7
|
+
Item {
|
|
8
|
+
id: root
|
|
9
|
+
|
|
10
|
+
required property var picker
|
|
11
|
+
required property var listing
|
|
12
|
+
required property var backend
|
|
13
|
+
required property var entry
|
|
14
|
+
|
|
15
|
+
// Recent is a location and not a path, so a relative line resolves against home there, never onto the location token.
|
|
16
|
+
readonly property string basePath: root.picker.recent ? root.picker.home : root.picker.path
|
|
17
|
+
|
|
18
|
+
// The path peeked and waiting for its answer.
|
|
19
|
+
property string asked: ""
|
|
20
|
+
// A typed file's path, waiting for its parent's listing to name its row, and that row once named.
|
|
21
|
+
property string leaf: ""
|
|
22
|
+
property string leafParent: ""
|
|
23
|
+
property int leafIndex: -1
|
|
24
|
+
// A listing that fails answers a waiting typed file with the footer's own error, so the leaf waits on nothing.
|
|
25
|
+
readonly property bool listingFailed: root.picker.listingFailed
|
|
26
|
+
onListingFailedChanged: if (root.listingFailed) root.leaf = ""
|
|
27
|
+
|
|
28
|
+
function commitTyped(text) {
|
|
29
|
+
var target = PathBar.resolve(text, root.basePath, root.picker.home)
|
|
30
|
+
if (target === "") {
|
|
31
|
+
// A URI on another host says its sentence; an empty line only closes, as the chrome's commit does.
|
|
32
|
+
if (PathBar.refused(text)) {
|
|
33
|
+
root.picker.say(PathBar.REFUSED_SENTENCE, false)
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
root.entry.closeEdit()
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
root.ask(target)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// A peek of count zero is the stat and nothing else; Tab's own peek asks for rows, so the two replies never mix.
|
|
43
|
+
function ask(target) {
|
|
44
|
+
root.asked = target
|
|
45
|
+
root.leaf = ""
|
|
46
|
+
root.listing.peek(target, 0, root.picker.showHidden)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function answered(path, readFailed, mode) {
|
|
50
|
+
if (path !== root.asked) return
|
|
51
|
+
root.asked = ""
|
|
52
|
+
var kind = PathBar.kindOfPeek(readFailed, mode)
|
|
53
|
+
if (kind === "missing") {
|
|
54
|
+
root.picker.say(Format.fill(PathBar.MISSING_SENTENCE, path), true)
|
|
55
|
+
} else if (kind === "folder") {
|
|
56
|
+
root.picker.open(path)
|
|
57
|
+
root.entry.closeEdit()
|
|
58
|
+
} else {
|
|
59
|
+
root.openFile(path)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function openFile(path) {
|
|
64
|
+
// A file where a folder answers is the backend's own refusal: the mark names the folder type and fails it, and a mark in flight keeps its accept.
|
|
65
|
+
if (root.picker.saving || root.picker.folderMode) {
|
|
66
|
+
root.picker.pathProbe = root.picker.check({op: "mark", path: path, directory: true, multiple: false})
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
root.leaf = path
|
|
70
|
+
root.leafParent = Picker.parentOf(path)
|
|
71
|
+
root.leafIndex = -1
|
|
72
|
+
if (root.leafParent !== root.picker.path) root.picker.open(root.leafParent)
|
|
73
|
+
else if (root.picker.listingFailed) root.leaf = ""
|
|
74
|
+
else if (root.picker.pendingListings === 0) root.listing.locate(path)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Sample input: {"t":"located","directory":"/d","path":"/d/a.txt","index":42}; -1 is a name the filter or the hidden files setting left out.
|
|
78
|
+
function located(message) {
|
|
79
|
+
// A reply against another listing is stale; the leaf's own listing locates it again when it lands.
|
|
80
|
+
if (root.leaf === "" || message.path !== root.leaf || message.directory !== root.leafParent) return
|
|
81
|
+
if (message.index < 0) {
|
|
82
|
+
root.picker.say(Format.fill(PathBar.FILTERED_SENTENCE, root.leaf.substring(root.leaf.lastIndexOf("/") + 1)), true)
|
|
83
|
+
root.leaf = ""
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
root.leafIndex = message.index
|
|
87
|
+
root.picker.cursorIndex = message.index
|
|
88
|
+
root.picker.viewItem().reshow(message.index)
|
|
89
|
+
root.take()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// The row's own accept runs once its window is held; the reshow above asks for that window when it is not.
|
|
93
|
+
function take() {
|
|
94
|
+
if (root.leaf === "" || root.leafIndex < 0 || !root.picker.rowFor(root.leafIndex)) return
|
|
95
|
+
var index = root.leafIndex
|
|
96
|
+
root.leaf = ""
|
|
97
|
+
root.leafIndex = -1
|
|
98
|
+
root.picker.activate(index)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The field closing, or the picker moving on, drops whatever was still being looked for.
|
|
102
|
+
function editClosed() {
|
|
103
|
+
root.asked = ""
|
|
104
|
+
root.leaf = ""
|
|
105
|
+
root.picker.focusDefault()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Every listing of the leaf's folder, its first or a re-read, renumbers the rows, so the leaf is located afresh against it.
|
|
109
|
+
Connections {
|
|
110
|
+
target: root.backend
|
|
111
|
+
function onListed() {
|
|
112
|
+
if (root.leaf === "") return
|
|
113
|
+
if (root.picker.path !== root.leafParent) { root.leaf = ""; return }
|
|
114
|
+
root.leafIndex = -1
|
|
115
|
+
root.listing.locate(root.leaf)
|
|
116
|
+
}
|
|
117
|
+
function onLocated(message) { root.located(message) }
|
|
118
|
+
function onRows() { root.take() }
|
|
119
|
+
function onJumped(id, favourites, zoxide, recent, frecency) {
|
|
120
|
+
if (root.entry.jump !== null) root.entry.jump.take(id, favourites, zoxide, recent, frecency)
|
|
121
|
+
}
|
|
122
|
+
function onPeeked(path, hidden, total, rows, readFailed, mode, hiddenLast, first) {
|
|
123
|
+
if (first === 0) root.answered(path, readFailed, mode)
|
|
124
|
+
else root.entry.completeWith(path, hidden, rows)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
import "js/Icons.js" as Icons
|
|
4
|
+
import "js/Picker.js" as Picker
|
|
5
|
+
import "js/Places.js" as Places
|
|
6
|
+
import "js/Keymap.js" as Keymap
|
|
7
|
+
|
|
8
|
+
// The chooser shares Flea's favourites and home locations without importing legacy GTK bookmarks.
|
|
9
|
+
Item {
|
|
10
|
+
id: root
|
|
11
|
+
|
|
12
|
+
property string home: ""
|
|
13
|
+
required property var picker
|
|
14
|
+
property string current: ""
|
|
15
|
+
// The ink the picker draws every rule in, handed down by ui/picker.qml, which says why; the
|
|
16
|
+
// window's own rule colour stands in so an unwired rail still draws a seam and never a black one.
|
|
17
|
+
property color edge: Theme.color.surface
|
|
18
|
+
|
|
19
|
+
signal chosen(string path)
|
|
20
|
+
signal networkCompleted(string requestId, string uri, bool success, string reason, bool connectFailed)
|
|
21
|
+
|
|
22
|
+
// SendPicker.html draws Recent above Home, and a save has no history to write into, so the one
|
|
23
|
+
// mode that cannot use the location does not offer it.
|
|
24
|
+
property bool offerRecent: true
|
|
25
|
+
|
|
26
|
+
// Recent is a location and not a path, so its row carries the token ui/js/Picker.js names; the
|
|
27
|
+
// rail's own cursor and activation then work on it exactly as they do on a favourite.
|
|
28
|
+
readonly property var recentRow: [{
|
|
29
|
+
path: Picker.RECENT, label: Picker.RECENT_LABEL, group: "favorite", kind: "favorite", glyph: "history"
|
|
30
|
+
}]
|
|
31
|
+
readonly property var placeEntries: (root.offerRecent ? root.recentRow : [])
|
|
32
|
+
.concat(Places.storedEntries(Flea.Favourites.records, root.home), Places.homeEntries(root.home, Flea.RailPlaces.probed, Icons.sidebarGlyphFor))
|
|
33
|
+
// Directive 55, from a user's report: a dialog that cannot reach a disk or a share is a
|
|
34
|
+
// dialog that makes you type the path, so the chooser draws the window's own NETWORK and DEVICES
|
|
35
|
+
// groups from the same listings. Rows only: nothing here trashes, ejects or renames.
|
|
36
|
+
// flea-qml: the drives found by looking close the list as plain folders, as they close the window's DEVICES group.
|
|
37
|
+
readonly property var entries: root.placeEntries.concat(network.entries, devices.entries, phones.entries, Places.volumeEntries(Flea.RailPlaces.probedVolumes, devices.entries))
|
|
38
|
+
|
|
39
|
+
implicitWidth: Math.min(parent.width / 3, Theme.space(172))
|
|
40
|
+
readonly property alias focusItem: rail
|
|
41
|
+
property bool awaitingNetwork: false
|
|
42
|
+
property string awaitingDevice: ""
|
|
43
|
+
|
|
44
|
+
Rectangle {
|
|
45
|
+
anchors.fill: parent
|
|
46
|
+
color: Theme.color.background
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Rectangle {
|
|
50
|
+
anchors.right: parent.right
|
|
51
|
+
width: Theme.spacing.hairline
|
|
52
|
+
height: parent.height
|
|
53
|
+
color: root.edge
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
ListView {
|
|
57
|
+
id: rail
|
|
58
|
+
anchors.fill: parent
|
|
59
|
+
anchors.topMargin: Theme.spacing.rowPaddingY
|
|
60
|
+
clip: true
|
|
61
|
+
boundsBehavior: Flickable.StopAtBounds
|
|
62
|
+
model: root.entries
|
|
63
|
+
activeFocusOnTab: true
|
|
64
|
+
currentIndex: 0
|
|
65
|
+
onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain)
|
|
66
|
+
Flea.FastScrollHandler { flickable: rail }
|
|
67
|
+
// No bar and no lane: rows fill the rail and still scroll by wheel, touchpad and keys.
|
|
68
|
+
Keys.onTabPressed: function(event) { root.picker.stepFocus(rail, (event.modifiers & Qt.ShiftModifier) !== 0) }
|
|
69
|
+
Keys.onBacktabPressed: root.picker.stepFocus(rail, true)
|
|
70
|
+
Keys.onPressed: function(event) {
|
|
71
|
+
var action = Keymap.lookup(event.key, event.text, event.modifiers, "rail")
|
|
72
|
+
event.accepted = true
|
|
73
|
+
if (action === "cursorUp") rail.currentIndex = Math.max(0, rail.currentIndex - 1)
|
|
74
|
+
else if (action === "cursorDown") rail.currentIndex = Math.min(root.entries.length - 1, rail.currentIndex + 1)
|
|
75
|
+
else if (action === "open" || action === "pageForward" || event.key === Qt.Key_Space) root.choose(rail.currentIndex)
|
|
76
|
+
else event.accepted = false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// index and modelData are required on ui/SidebarRow.qml itself, so the view fills them;
|
|
80
|
+
// redeclaring them here left the delegate uninitialised and the rail drew nothing.
|
|
81
|
+
delegate: Flea.SidebarRow {
|
|
82
|
+
lit: rail.activeFocus ? index === rail.currentIndex : modelData.path === root.current
|
|
83
|
+
focused: rail.activeFocus
|
|
84
|
+
onActivated: function (at) { root.choose(at) }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function choose(index) {
|
|
89
|
+
if (root.picker.submitting || root.picker.backendUnavailable) return
|
|
90
|
+
var entry = root.entries[index]
|
|
91
|
+
if (!entry) return
|
|
92
|
+
if (entry.error) { root.picker.say(entry.error, true); return }
|
|
93
|
+
root.awaitingNetwork = false
|
|
94
|
+
// The three groups below the places, routed the way ui/Sidebar.qml routes them.
|
|
95
|
+
if (entry.group === "device" && entry.kind === "phone") {
|
|
96
|
+
root.awaitingNetwork = true
|
|
97
|
+
network.openShare(entry.uri, entry.mounted, entry.label)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
if (entry.group === "device") {
|
|
101
|
+
// The row this dialog is waiting on, by device: any other mount's open is not its answer.
|
|
102
|
+
root.awaitingDevice = entry.device
|
|
103
|
+
devices.activate(index - root.placeEntries.length - network.entries.length)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
if (entry.group === "network") {
|
|
107
|
+
root.awaitingNetwork = true
|
|
108
|
+
network.activate(index - root.placeEntries.length)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
if (entry.path.indexOf("file://") === 0) {
|
|
112
|
+
try {
|
|
113
|
+
var path = decodeURIComponent(entry.path.substring(7))
|
|
114
|
+
if (path.charAt(0) !== "/" || path.indexOf("\0") >= 0) throw new Error("not a local file URI")
|
|
115
|
+
root.chosen(path)
|
|
116
|
+
} catch (error) { root.picker.say("This favorite points to a location Flea cannot open.", true) }
|
|
117
|
+
} else if (entry.path.indexOf("://") >= 0) {
|
|
118
|
+
root.awaitingNetwork = true
|
|
119
|
+
network.openShare(entry.path, false, entry.label, false)
|
|
120
|
+
} else root.chosen(entry.path)
|
|
121
|
+
}
|
|
122
|
+
// Only the row the dialog asked for answers it, which the device says and a path alone cannot.
|
|
123
|
+
function deviceOpened(path) {
|
|
124
|
+
var awaited = root.awaitingDevice
|
|
125
|
+
if (awaited.length === 0) return
|
|
126
|
+
for (var i = 0; i < devices.entries.length; i++) {
|
|
127
|
+
if (devices.entries[i].device === awaited && devices.entries[i].path === path) {
|
|
128
|
+
root.awaitingDevice = ""
|
|
129
|
+
root.chosen(path)
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function openChild(uri, label) { network.openChildShare(uri, label) }
|
|
136
|
+
function retry(requestId, uri, label, password) { root.awaitingNetwork = true; network.saveLocation(uri, label, password, requestId) }
|
|
137
|
+
function cancelNetwork(requestId) {
|
|
138
|
+
root.awaitingNetwork = false
|
|
139
|
+
network.cancelLocation(requestId)
|
|
140
|
+
}
|
|
141
|
+
Connections {
|
|
142
|
+
target: root.picker
|
|
143
|
+
function onPathChanged() { root.awaitingNetwork = false; root.awaitingDevice = "" }
|
|
144
|
+
function onBackendUnavailableChanged() {
|
|
145
|
+
if (root.picker.backendUnavailable) { root.awaitingNetwork = false; root.awaitingDevice = "" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function controls() {
|
|
149
|
+
var out = []
|
|
150
|
+
for (var i = 0; i < rail.count; i++) {
|
|
151
|
+
var item = rail.itemAtIndex(i)
|
|
152
|
+
if (item) out.push(root.picker.control(root.entries[i].label, item, !root.entries[i].error && !root.picker.submitting && !root.picker.backendUnavailable))
|
|
153
|
+
}
|
|
154
|
+
return out
|
|
155
|
+
}
|
|
156
|
+
// Standing, not loaded on demand: its own listing is what the NETWORK rows are, and the window
|
|
157
|
+
// pays the same five-second rhythm for them.
|
|
158
|
+
Flea.NetworkMounts {
|
|
159
|
+
id: network
|
|
160
|
+
savedFavourites: Flea.Favourites.records
|
|
161
|
+
onCompleted: function(requestId, uri, success, reason, connectFailed) { root.networkCompleted(requestId, uri, success, reason, connectFailed) }
|
|
162
|
+
onOpened: function(path) { if (root.awaitingNetwork) root.chosen(path) }
|
|
163
|
+
// A FUSE path that is a file: the chooser selects it, the way a favourite file resolves.
|
|
164
|
+
onOpenFileRequested: function(path) { if (root.awaitingNetwork) root.chosen(path) }
|
|
165
|
+
onMessage: function(text, error) { root.picker.say(text, error) }
|
|
166
|
+
onRetryRequested: function(uri, label, password, reason, failed) {
|
|
167
|
+
if (root.awaitingNetwork) root.picker.retryNetwork(uri, label, password, reason, failed)
|
|
168
|
+
}
|
|
169
|
+
onSharesListed: function(uri, label, names) {
|
|
170
|
+
if (root.awaitingNetwork) root.picker.showShares(uri, label, names)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Flea.DeviceMounts {
|
|
175
|
+
id: devices
|
|
176
|
+
onOpened: function (path) { root.deviceOpened(path) }
|
|
177
|
+
// A message carries no device, so it cannot end a wait: only the awaited device's own open does.
|
|
178
|
+
onMessage: function (text, error) { root.picker.say(text, error) }
|
|
179
|
+
// The picker's one-line message has no detail view, so the headline stands alone.
|
|
180
|
+
onOperationResult: function (headline, detail, error) { root.picker.say(headline, error) }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// The phone rows the window draws, read off the same gio listing; their mount is the share leg.
|
|
184
|
+
Flea.PhoneMounts {
|
|
185
|
+
id: phones
|
|
186
|
+
listingText: network.mountListing
|
|
187
|
+
onMessage: function (text, error) { root.picker.say(text, error) }
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Quickshell
|
|
2
|
+
import Quickshell.Io
|
|
3
|
+
import QtQuick
|
|
4
|
+
import QtQml.XmlListModel
|
|
5
|
+
import "js/Format.js" as Format
|
|
6
|
+
import "js/Recent.js" as Recent
|
|
7
|
+
|
|
8
|
+
// The desktop's own recent history, read and never written. Qt's XML reader does the parsing, so
|
|
9
|
+
// this file has no parser of its own: XmlListModel is QXmlStreamReader behind a query, and a
|
|
10
|
+
// history Flea hand-parsed would be a second XBEL implementation on a file it does not own.
|
|
11
|
+
// The read is lazy, so a picker whose user never asks for Recent never opens the file at all.
|
|
12
|
+
QtObject {
|
|
13
|
+
id: root
|
|
14
|
+
|
|
15
|
+
// The validated absolute paths, newest first; empty until refresh() has been asked for.
|
|
16
|
+
property var paths: []
|
|
17
|
+
property var visits: ({})
|
|
18
|
+
signal refreshed()
|
|
19
|
+
|
|
20
|
+
readonly property string file: Recent.historyPath(Quickshell.env("XDG_DATA_HOME"), Quickshell.env("HOME"))
|
|
21
|
+
function joinRequesters(current, requester) { return Recent.joinRequesters(current, requester) }
|
|
22
|
+
|
|
23
|
+
// Asked for when the Recent location is opened, so the file is re-read rather than remembered:
|
|
24
|
+
// every other application on the box appends to it while this window is up.
|
|
25
|
+
function refresh() {
|
|
26
|
+
if (historyFile.path === root.file) historyFile.reload()
|
|
27
|
+
else historyFile.path = root.file
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Read first so only an absent file is quiet: XmlListModel warns on one, and a fresh account has none.
|
|
31
|
+
property FileView historyFile: FileView {
|
|
32
|
+
printErrors: false
|
|
33
|
+
onLoaded: root.parse()
|
|
34
|
+
onLoadFailed: function (error) {
|
|
35
|
+
if (error !== FileViewError.FileNotFound)
|
|
36
|
+
console.warn("PickerRecent: could not read " + root.file + ": " + error)
|
|
37
|
+
root.paths = []
|
|
38
|
+
root.visits = ({})
|
|
39
|
+
root.refreshed()
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The model re-reads what the check just read, so a present history is a second read from the page cache.
|
|
44
|
+
function parse() {
|
|
45
|
+
var url = Format.fileUri(root.file)
|
|
46
|
+
if (history.source.toString() === url) history.reload()
|
|
47
|
+
else history.source = url
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
property XmlListModel historyModel: XmlListModel {
|
|
51
|
+
id: history
|
|
52
|
+
query: "/xbel/bookmark"
|
|
53
|
+
// Never on the construction-time Null: that answered refreshed with no rows, and the path jump takes the first answer.
|
|
54
|
+
onStatusChanged: if (status === XmlListModel.Ready || status === XmlListModel.Error) root.rebuild()
|
|
55
|
+
XmlListModelRole { name: "href"; attributeName: "href" }
|
|
56
|
+
XmlListModelRole { name: "visited"; attributeName: "visited" }
|
|
57
|
+
XmlListModelRole { name: "modified"; attributeName: "modified" }
|
|
58
|
+
XmlListModelRole { name: "added"; attributeName: "added" }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Qt 6 gives XmlListModel no get(), so the rows are read off the model itself: data() is
|
|
62
|
+
// invokable and the roles are numbered from Qt.UserRole in the order declared above.
|
|
63
|
+
readonly property int hrefRole: Qt.UserRole
|
|
64
|
+
readonly property int visitedRole: Qt.UserRole + 1
|
|
65
|
+
readonly property int modifiedRole: Qt.UserRole + 2
|
|
66
|
+
readonly property int addedRole: Qt.UserRole + 3
|
|
67
|
+
|
|
68
|
+
// Every bookmark is read, because ui/js/Recent.js LIMIT bounds the rail and not the read: XBEL
|
|
69
|
+
// promises no order, so a bound in file order hands the sort an arbitrary slice and lists the
|
|
70
|
+
// newest of that. Measured on oldest-first fixtures, six batches whole against two capped: a
|
|
71
|
+
// rebuild costs about 3 ms at 500 bookmarks whichever way, tens of ms against 3 at 5,000, and
|
|
72
|
+
// hundreds of ms against 4 at 50,000, over an XmlListModel parse both pay that is itself a
|
|
73
|
+
// sixth of a second there. The Instantiator this replaced cost seconds at 50,000.
|
|
74
|
+
function rebuild() {
|
|
75
|
+
var found = []
|
|
76
|
+
var wanted = history.count
|
|
77
|
+
for (var i = 0; i < wanted; i++) {
|
|
78
|
+
var at = history.index(i, 0)
|
|
79
|
+
// visited is when the file itself was last opened, which is what Recent means; the
|
|
80
|
+
// other two stamp the bookmark and stand in for a writer that left visited out.
|
|
81
|
+
var visited = String(history.data(at, root.visitedRole) || "")
|
|
82
|
+
var modified = String(history.data(at, root.modifiedRole) || "")
|
|
83
|
+
var added = String(history.data(at, root.addedRole) || "")
|
|
84
|
+
found.push({ href: String(history.data(at, root.hrefRole) || ""),
|
|
85
|
+
stamp: visited.length > 0 ? visited : (modified.length > 0 ? modified : added) })
|
|
86
|
+
}
|
|
87
|
+
var entries = Recent.entries(found)
|
|
88
|
+
root.visits = Recent.visits(entries)
|
|
89
|
+
root.paths = entries.map(function (entry) { return entry.path })
|
|
90
|
+
root.refreshed()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import QtQuick
|
|
2
|
+
import "." as Flea
|
|
3
|
+
import "js/Format.js" as Format
|
|
4
|
+
import "js/Picker.js" as Picker
|
|
5
|
+
|
|
6
|
+
// The save area: Filename and its field in one column, and under it the collision review's error line.
|
|
7
|
+
Item {
|
|
8
|
+
id: root
|
|
9
|
+
required property var picker
|
|
10
|
+
property Item keyTarget: null
|
|
11
|
+
// Only keys left unhandled by Filename reach the listing, after editing and the field's Tab walk.
|
|
12
|
+
Keys.forwardTo: root.keyTarget ? [root.keyTarget] : []
|
|
13
|
+
signal nameEdited(string text)
|
|
14
|
+
signal accepted()
|
|
15
|
+
readonly property alias fieldItem: field
|
|
16
|
+
readonly property alias scrollItem: body
|
|
17
|
+
readonly property alias statusItem: statusLine
|
|
18
|
+
readonly property string askedName: root.picker.saveName || root.picker.req.name
|
|
19
|
+
readonly property bool refused: root.askedName.length > 0 && !Picker.validName(root.askedName)
|
|
20
|
+
visible: root.picker.saving
|
|
21
|
+
property real maximumHeight: parent.height
|
|
22
|
+
// The answer row below is the column's last row and brings the gap under the field; the column keeps only its top padding.
|
|
23
|
+
implicitHeight: visible ? Math.min(maximumHeight, body.wanted + Theme.spacing.rowPaddingX) : 0
|
|
24
|
+
|
|
25
|
+
Rectangle { anchors.fill: parent; color: Theme.color.surface }
|
|
26
|
+
Rectangle {
|
|
27
|
+
width: parent.width
|
|
28
|
+
height: Theme.spacing.hairline
|
|
29
|
+
color: root.picker.edge
|
|
30
|
+
}
|
|
31
|
+
Flea.CardScroll {
|
|
32
|
+
id: body
|
|
33
|
+
bleed: Theme.focusClearance
|
|
34
|
+
anchors.fill: parent
|
|
35
|
+
anchors.margins: Theme.spacing.rowPaddingX - Theme.focusClearance
|
|
36
|
+
anchors.bottomMargin: -Theme.focusClearance
|
|
37
|
+
Column {
|
|
38
|
+
id: column
|
|
39
|
+
width: parent.width
|
|
40
|
+
spacing: Theme.spacing.gap
|
|
41
|
+
|
|
42
|
+
Text {
|
|
43
|
+
text: "Filename"
|
|
44
|
+
textFormat: Text.PlainText
|
|
45
|
+
color: Theme.color.foreground
|
|
46
|
+
font { family: Theme.font.family; pixelSize: Theme.font.caption }
|
|
47
|
+
}
|
|
48
|
+
Rectangle {
|
|
49
|
+
width: parent.width
|
|
50
|
+
height: Math.max(Theme.hitMin, Theme.rowHeight - Theme.spacing.rowPaddingY)
|
|
51
|
+
color: Theme.color.background
|
|
52
|
+
border.width: Theme.spacing.hairline
|
|
53
|
+
// Muted at rest, accent on focus: the frame DialogField, MenuActionDialog, OpenWithDialog and PermissionsDialog draw.
|
|
54
|
+
border.color: field.activeFocus ? Theme.color.accent : Theme.color.mutedFrame
|
|
55
|
+
TextInput {
|
|
56
|
+
id: field
|
|
57
|
+
anchors.fill: parent
|
|
58
|
+
anchors.leftMargin: Theme.spacing.gap
|
|
59
|
+
anchors.rightMargin: Theme.spacing.gap
|
|
60
|
+
verticalAlignment: TextInput.AlignVCenter
|
|
61
|
+
text: root.picker.saveName
|
|
62
|
+
color: Theme.color.foreground
|
|
63
|
+
selectionColor: Theme.color.accent
|
|
64
|
+
selectedTextColor: Theme.color.background
|
|
65
|
+
font { family: Theme.font.family; pixelSize: Theme.font.body }
|
|
66
|
+
clip: true
|
|
67
|
+
activeFocusOnTab: true
|
|
68
|
+
enabled: !root.picker.submitting
|
|
69
|
+
Accessible.name: "Filename"
|
|
70
|
+
onTextEdited: root.nameEdited(text)
|
|
71
|
+
onAccepted: root.accepted()
|
|
72
|
+
// Enter remains the field's save action when it opens a collision review.
|
|
73
|
+
Keys.onReturnPressed: root.accepted()
|
|
74
|
+
Keys.onEnterPressed: root.accepted()
|
|
75
|
+
Keys.onTabPressed: function(event) { root.picker.stepFocus(field, (event.modifiers & Qt.ShiftModifier) !== 0) }
|
|
76
|
+
Keys.onBacktabPressed: root.picker.stepFocus(field, true)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
Text {
|
|
80
|
+
id: statusLine
|
|
81
|
+
width: parent.width
|
|
82
|
+
visible: text.length > 0
|
|
83
|
+
text: root.refused ? Format.fill(Picker.SAVE_REFUSED, root.askedName, Picker.NAME_REFUSED)
|
|
84
|
+
: root.picker.saveError || (root.picker.collisionReview ? Picker.collisionLine(Picker.jointName(root.picker.saveName)) : "")
|
|
85
|
+
textFormat: Text.PlainText
|
|
86
|
+
wrapMode: Text.Wrap
|
|
87
|
+
color: Theme.color.errorOnSurface
|
|
88
|
+
font { family: Theme.font.family; pixelSize: Theme.font.caption }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// The review returns the keyboard to the field with the stem selected and the caret at its end.
|
|
93
|
+
function selectStem() {
|
|
94
|
+
field.forceActiveFocus(Qt.TabFocusReason)
|
|
95
|
+
field.select(0, Picker.stemEnd(field.text))
|
|
96
|
+
}
|
|
97
|
+
function focusItems() { return root.visible ? [field] : [] }
|
|
98
|
+
function controls() {
|
|
99
|
+
return root.focusItems().map(function(item) { return root.picker.control("Filename", item, item.enabled) })
|
|
100
|
+
}
|
|
101
|
+
}
|