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
package/ui/js/Thumbs.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// A row this listing has dealt with: null means asked and waiting, a string is the answer.
|
|
4
|
+
// A cache-only ask carries a mark, so its empty answer means "not tried" and a class switch forgets it.
|
|
5
|
+
var ASKED = null
|
|
6
|
+
var CACHE_ASKED = "cache-asked"
|
|
7
|
+
var CACHE_MISS = "cache-missed"
|
|
8
|
+
|
|
9
|
+
// Asked and not answered yet, a plain ask or a cache-only one; only these have a job to cancel.
|
|
10
|
+
function pending(value) {
|
|
11
|
+
return value === ASKED || value === CACHE_ASKED
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function empty() {
|
|
15
|
+
return { file: {}, order: [] }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The rows a request may name: the viewport in row indices, clamped to the last row of the listing.
|
|
19
|
+
function viewport(contentY, rowHeight, visibleRows, total) {
|
|
20
|
+
var first = Math.floor(contentY / rowHeight)
|
|
21
|
+
// A window that is not row aligned straddles one more row than it holds, so the bottom comes off its pixel extent.
|
|
22
|
+
var bottom = contentY + visibleRows * rowHeight
|
|
23
|
+
return { first: first, last: Math.min(total - 1, Math.ceil(bottom / rowHeight) - 1) }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Only visible rows, only rows the backend called thumbnailable, only rows never asked.
|
|
27
|
+
function plan(state, rows, held, first, last, mode) {
|
|
28
|
+
var ask = []
|
|
29
|
+
for (var i = first; i <= last; i++) {
|
|
30
|
+
if (state.file[i] !== undefined) {
|
|
31
|
+
continue
|
|
32
|
+
}
|
|
33
|
+
var row = rows[i - held]
|
|
34
|
+
if (allowed(row, mode)) {
|
|
35
|
+
ask.push(i)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
var drop = []
|
|
39
|
+
for (var key in state.file) {
|
|
40
|
+
if (!pending(state.file[key])) {
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
var at = Number(key)
|
|
44
|
+
if (at < first || at > last || !allowed(rows[at - held], mode)) {
|
|
45
|
+
drop.push(at)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { ask: ask, drop: drop }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// A cancelled row is forgotten here too, because the backend forgets it when it drops the job.
|
|
52
|
+
function forget(state, row) {
|
|
53
|
+
delete state.file[row]
|
|
54
|
+
var at = state.order.indexOf(row)
|
|
55
|
+
if (at >= 0) {
|
|
56
|
+
state.order.splice(at, 1)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// A miss on a generating class returns to unasked; on a cache-only class it stays a miss.
|
|
61
|
+
function miss(state, row, generating, cap) {
|
|
62
|
+
if (generating === true) {
|
|
63
|
+
forget(state, row)
|
|
64
|
+
return { file: state.file, order: state.order }
|
|
65
|
+
}
|
|
66
|
+
return remember(state, row, "", cap)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The wrapper is new so a QML binding on it re-evaluates; the map inside is mutated in place.
|
|
70
|
+
function applied(state, work) {
|
|
71
|
+
for (var i = 0; i < work.drop.length; i++) {
|
|
72
|
+
forget(state, work.drop[i])
|
|
73
|
+
}
|
|
74
|
+
var mark = work.cacheOnly === true ? CACHE_ASKED : ASKED
|
|
75
|
+
for (var j = 0; j < work.ask.length; j++) {
|
|
76
|
+
state.file[work.ask[j]] = mark
|
|
77
|
+
state.order.push(work.ask[j])
|
|
78
|
+
}
|
|
79
|
+
return { file: state.file, order: state.order }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A live answer re-lists some rows: each finished thumbnail follows its row, by name, to that row's new index. Asked and missed rows do not follow.
|
|
83
|
+
function carried(state, oldRows, oldHeld, newRows, newHeld) {
|
|
84
|
+
var at = Object.create(null)
|
|
85
|
+
for (var j = 0; j < newRows.length; j++) {
|
|
86
|
+
if (newRows[j].t) {
|
|
87
|
+
at[newRows[j].n] = newHeld + j
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
var next = empty()
|
|
91
|
+
for (var k = 0; k < state.order.length; k++) {
|
|
92
|
+
var file = state.file[state.order[k]]
|
|
93
|
+
var row = oldRows[state.order[k] - oldHeld]
|
|
94
|
+
if (file && file !== CACHE_ASKED && file !== CACHE_MISS && row && at[row.n] !== undefined) {
|
|
95
|
+
next.file[at[row.n]] = file
|
|
96
|
+
next.order.push(at[row.n])
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return next
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The cap bounds a policy bug, not normal use: only the rows a viewport held are ever recorded.
|
|
103
|
+
function remember(state, row, file, cap) {
|
|
104
|
+
if (state.file[row] === undefined) {
|
|
105
|
+
state.order.push(row)
|
|
106
|
+
}
|
|
107
|
+
// An empty answer to a cache-only ask was never a decode, so it keeps its mark.
|
|
108
|
+
state.file[row] = (file === "" && state.file[row] === CACHE_ASKED) ? CACHE_MISS : file
|
|
109
|
+
while (state.order.length > cap) {
|
|
110
|
+
delete state.file[state.order.shift()]
|
|
111
|
+
}
|
|
112
|
+
return { file: state.file, order: state.order }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function fileFor(state, row) {
|
|
116
|
+
var value = state.file[row]
|
|
117
|
+
if (value === CACHE_ASKED || value === CACHE_MISS) return ""
|
|
118
|
+
return typeof value === "string" ? value : ""
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Answered with nothing, which is not the same as not answered yet. A thumbnailer that could not
|
|
122
|
+
// read the file reports an empty name, and only once that has arrived is it honest to say so.
|
|
123
|
+
function refused(state, row) {
|
|
124
|
+
return state.file[row] === ""
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/backend/thumbs.rs THUMB_SIZE: the longest side of a cache file made from a larger original.
|
|
128
|
+
var CACHE_SIZE = 256
|
|
129
|
+
|
|
130
|
+
// How much a picture may be enlarged to fit its box, and never past limit: 1 for an original, which
|
|
131
|
+
// draws at its own size when it is smaller than the box (decided 2026-09-24). Sample input: (754, 471, 120, 68, 1) is 1.
|
|
132
|
+
function fitScale(boxWidth, boxHeight, width, height, limit) {
|
|
133
|
+
return Math.min(limit, boxWidth / Math.max(1, width), boxHeight / Math.max(1, height))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// A cache file stands in for its original, so it is enlarged at most to the original's own size. The
|
|
137
|
+
// longest side is compared because EXIF can turn either one. Unknown dimensions leave a full-size cache
|
|
138
|
+
// file free to fill the box, while a smaller one already is the original. Sample input: (256, 171, 300, 200) is 300/256.
|
|
139
|
+
function thumbLimit(thumbWidth, thumbHeight, originalWidth, originalHeight) {
|
|
140
|
+
var thumbLongest = Math.max(1, thumbWidth, thumbHeight)
|
|
141
|
+
var originalLongest = Math.max(Number(originalWidth) || 0, Number(originalHeight) || 0)
|
|
142
|
+
if (originalLongest > 0)
|
|
143
|
+
return originalLongest / thumbLongest
|
|
144
|
+
return thumbLongest < CACHE_SIZE ? 1 : Infinity
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// A video poster fills the frame; an image keeps the never-enlarge cap.
|
|
148
|
+
function posterLimit(isVideo, thumbWidth, thumbHeight, originalWidth, originalHeight) {
|
|
149
|
+
if (isVideo)
|
|
150
|
+
return Infinity
|
|
151
|
+
return thumbLimit(thumbWidth, thumbHeight, originalWidth, originalHeight)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Backend icon identity distinguishes image thumbnails from video without opening any extra file; an AppImage's own icon is its identity, so Images draws a row the backend flagged a.
|
|
155
|
+
function allowed(row, mode) {
|
|
156
|
+
if (!row || !row.t || mode === "off") return false
|
|
157
|
+
return mode !== "images" || row.i === "image-x-generic" || row.a === true
|
|
158
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// The model behind ui/TransferCard.qml, kept out of the QML so every state the card can draw is
|
|
6
|
+
// checked without a window. The object each function takes is ui/js/Ops.js's transfer.
|
|
7
|
+
|
|
8
|
+
// Whole phrases with "%1" for a value, filled by fill so each translates as one; a leading space is the join to the figure drawn before it.
|
|
9
|
+
var WORDS = {
|
|
10
|
+
movedWaiting: " moved · waiting for the drive",
|
|
11
|
+
copiedWaiting: " copied · waiting for the drive",
|
|
12
|
+
moved: " moved",
|
|
13
|
+
copied: " copied",
|
|
14
|
+
writingTo: "Writing to %1",
|
|
15
|
+
moving: "Moving %1 of %2",
|
|
16
|
+
copying: "Copying %1 of %2",
|
|
17
|
+
redoing: "Redoing %3 %1 of %2"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// The byte sample for the item in flight. done is the count already finished, so it stays where it
|
|
21
|
+
// was: a sample fills the item in, it does not complete it.
|
|
22
|
+
function sampled(t, index, name, bytes, total, scanned) {
|
|
23
|
+
// A sample carrying none must not unset the total an earlier one already brought.
|
|
24
|
+
var settled = scanned > 0 ? scanned : (t.scanned || 0)
|
|
25
|
+
// A sample for an item itemDone already closed carries the bytes paced lines left out, so they join moved and the card stays put.
|
|
26
|
+
if (index < (t.done || 0))
|
|
27
|
+
return Object.assign({}, t, {moved: (t.moved || 0) + bytes, scanned: settled})
|
|
28
|
+
return Object.assign({}, t, {index: index, name: name, done: index, bytes: bytes, total: total, scanned: settled})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// That item's own terminal line: it counts whole from here, and its byte sample is spent. What it
|
|
32
|
+
// moved joins the running sum first, as the size the wire last named for it, or as the bytes it
|
|
33
|
+
// reported when it never had a total, which is what a directory's own running count is. An item
|
|
34
|
+
// that emitted no sample at all adds nothing, which undercounts a small file and never overcounts.
|
|
35
|
+
function itemDone(t, index, name) {
|
|
36
|
+
var sampled = t.index === index ? (t.total > 0 ? t.total : t.bytes) : 0
|
|
37
|
+
return Object.assign({}, t, {index: index, name: name, done: index + 1, bytes: 0, total: 0,
|
|
38
|
+
moved: (t.moved || 0) + sampled})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Sample input: itemsDone(t, [{index: 0, name: "a"}, {index: 1, name: "b"}]) is itemDone(itemDone(t, 0, "a"), 1, "b") in one write.
|
|
42
|
+
function itemsDone(t, held) {
|
|
43
|
+
var next = t
|
|
44
|
+
for (var i = 0; i < held.length; i++)
|
|
45
|
+
next = itemDone(next, held[i].index, held[i].name)
|
|
46
|
+
return next
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// TransferCard rule 2: what the whole transfer has moved, the finished items plus the one in flight.
|
|
50
|
+
function movedBytes(t) {
|
|
51
|
+
return (t.moved || 0) + (t.bytes || 0)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Durability: the backend's last directory flush. Sample input:
|
|
55
|
+
// {"t":"transferprogress","id":12,"index":0,"name":"","bytes":0,"total":0,"scanned":0,"phase":"writing","drive":"128GB"}
|
|
56
|
+
// Every file is already complete, so the counted bytes stay and the sample is spent.
|
|
57
|
+
function markWriting(t, drive) {
|
|
58
|
+
return Object.assign({}, t, {writing: true, drive: drive || "", name: "", bytes: 0, total: 0})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// A surface that pre-renders its own Cancel state carries noCancel; see ui/js/FlashCard.js.
|
|
62
|
+
function cancelEnabled(t) {
|
|
63
|
+
if (t.noCancel === true)
|
|
64
|
+
return false
|
|
65
|
+
// The flush dims Cancel because every file is already complete, so there is nothing left to stop.
|
|
66
|
+
return t.running === true && t.writing !== true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// TransferCard rule 1's line under the bar, as the pieces it is drawn from: a figure the card inks
|
|
70
|
+
// in the foreground, or the muted words between them.
|
|
71
|
+
function byteParts(t, rate) {
|
|
72
|
+
// A surface that pre-renders its own line carries byteText; see ui/js/FlashCard.js.
|
|
73
|
+
if (t.byteText !== undefined)
|
|
74
|
+
return t.byteText
|
|
75
|
+
// The flush has no percentage because the kernel reports none: the confirmed bytes and the wait.
|
|
76
|
+
if (t.writing === true) {
|
|
77
|
+
var confirmed = movedBytes(t)
|
|
78
|
+
if (confirmed <= 0) {
|
|
79
|
+
return []
|
|
80
|
+
}
|
|
81
|
+
return [figure(Format.size(confirmed)), word(t.moving ? WORDS.movedWaiting : WORDS.copiedWaiting)]
|
|
82
|
+
}
|
|
83
|
+
var moved = movedBytes(t)
|
|
84
|
+
if (moved <= 0) {
|
|
85
|
+
return []
|
|
86
|
+
}
|
|
87
|
+
var total = transferTotal(t)
|
|
88
|
+
var parts = [figure(Format.size(moved))]
|
|
89
|
+
if (total > 0) {
|
|
90
|
+
parts.push(word(" of "), figure(Format.size(total)))
|
|
91
|
+
} else {
|
|
92
|
+
parts.push(word(t.moving ? WORDS.moved : WORDS.copied))
|
|
93
|
+
}
|
|
94
|
+
parts.push(word(" · "), figure(Format.size(rate) + "/s"))
|
|
95
|
+
// Rule 3b: an estimate needs both a total and a rate, and a stall has neither to divide by.
|
|
96
|
+
if (total > 0 && rate > 0) {
|
|
97
|
+
// Clamped: the sweep and the copy count separately, so a file appended mid-copy can pass it.
|
|
98
|
+
parts.push(word(" · "), figure(Format.duration(Math.max(0, total - moved) / rate * 1000)), word(" left"))
|
|
99
|
+
}
|
|
100
|
+
return parts
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function figure(text) {
|
|
104
|
+
return { text: text, figure: true }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function word(text) {
|
|
108
|
+
return { text: text, figure: false }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The card's headline, the count with no name in it: the card gives the name a row of its own, and
|
|
112
|
+
// ui/js/Ops.js builds the status bar's one-line form from this same string.
|
|
113
|
+
function head(t) {
|
|
114
|
+
// A surface that pre-renders its own headline carries headText; see ui/js/FlashCard.js.
|
|
115
|
+
if (t.headText !== undefined)
|
|
116
|
+
return t.headText
|
|
117
|
+
// The flush headline names the drive being waited on, with no percentage, because the kernel reports none.
|
|
118
|
+
if (t.writing === true)
|
|
119
|
+
return Format.fill(WORDS.writingTo, t.drive && t.drive.length > 0 ? t.drive : "the drive")
|
|
120
|
+
// An extract has no items to count, so it names its verb alone.
|
|
121
|
+
if (t.extract)
|
|
122
|
+
return "Extracting"
|
|
123
|
+
return Format.fill(t.redo ? WORDS.redoing : t.moving ? WORDS.moving : WORDS.copying, Format.count(t.index + 1), Format.count(t.n), t.redo)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// The card's second row: the item in flight and how big it is. total is 0 for a directory, whose
|
|
127
|
+
// size is not known in advance without a sweep, so that one names itself and claims nothing more:
|
|
128
|
+
// the running count it does have is the byte line's under the bar, and saying it twice is the thing
|
|
129
|
+
// this canvas exists to stop.
|
|
130
|
+
function fileLine(t) {
|
|
131
|
+
// A surface that pre-renders its own file line carries fileText; see ui/js/FlashCard.js.
|
|
132
|
+
if (t.fileText !== undefined)
|
|
133
|
+
return t.fileText
|
|
134
|
+
// The flush carries no file line: every file is already complete.
|
|
135
|
+
if (t.writing === true) {
|
|
136
|
+
return ""
|
|
137
|
+
}
|
|
138
|
+
if (t.name.length === 0) {
|
|
139
|
+
return ""
|
|
140
|
+
}
|
|
141
|
+
return t.total > 0 ? t.name + " · " + Format.size(t.total) : t.name
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The bar is the whole transfer, never the one file: done carries the items already finished and
|
|
145
|
+
// the byte sample only fills in the one in flight. One large file is then its own byte bar, and
|
|
146
|
+
// thirty thousand small ones step it once each instead of restarting it thirty thousand times.
|
|
147
|
+
// t.total is the item in flight's, so it is the transfer's only when the transfer is that one
|
|
148
|
+
// item. One directory is n === 1 with no total of its own, and the sweep is what answers for it.
|
|
149
|
+
function transferTotal(t) {
|
|
150
|
+
return t.n === 1 && t.total > 0 ? t.total : (t.scanned || 0)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// A bar figure held to 0 through 1, where a missing or non-numeric one reads as empty.
|
|
154
|
+
function unit(value) {
|
|
155
|
+
if (typeof value !== "number" || !(value > 0))
|
|
156
|
+
return 0
|
|
157
|
+
return value > 1 ? 1 : value
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The bar fills against the same total the line under it names, so the two always agree; with no
|
|
161
|
+
// total yet it counts finished items plus the one in flight's share, as it always did.
|
|
162
|
+
function fraction(t) {
|
|
163
|
+
// A surface that pre-renders its own share carries it; see ui/js/FlashCard.js.
|
|
164
|
+
if (t.share !== undefined)
|
|
165
|
+
return unit(t.share)
|
|
166
|
+
if (t.n <= 0) {
|
|
167
|
+
return 0
|
|
168
|
+
}
|
|
169
|
+
var total = transferTotal(t)
|
|
170
|
+
var at = total > 0 ? movedBytes(t) / total : (t.done + (t.total > 0 ? t.bytes / t.total : 0)) / t.n
|
|
171
|
+
return unit(at)
|
|
172
|
+
}
|
package/ui/js/Trash.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "KeyName.js" as KeyName
|
|
4
|
+
.import "Format.js" as Format
|
|
5
|
+
.import "Ops.js" as Ops
|
|
6
|
+
|
|
7
|
+
// Whole sentences with "%1" for the key that arms and "%2" for the key that acts on its own, filled by Format.fill.
|
|
8
|
+
var WORDS = {
|
|
9
|
+
trash: "Press %1 again to move it to Trash, or %2 on its own.",
|
|
10
|
+
review: "Press %1 again to review permanent deletion, or %2 on its own.",
|
|
11
|
+
trashAlone: "Press %1 again to move it to Trash.",
|
|
12
|
+
trashMany: "Press %1 again to move them to Trash, or %2 on its own.",
|
|
13
|
+
trashManyAlone: "Press %1 again to move them to Trash.",
|
|
14
|
+
reviewAlone: "Press %1 again to review permanent deletion.",
|
|
15
|
+
deleteOne: "Delete 1 item permanently?",
|
|
16
|
+
deleteMany: "Delete %1 items permanently?"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// The keys come from the running preset: the arming key, and the key that acts at once when the preset binds one; the count of rows the pair would trash picks it or them.
|
|
20
|
+
function trashPrompt(count) {
|
|
21
|
+
var alone = KeyName.keyOf("trash", "none"), many = count > 1
|
|
22
|
+
if (alone.length > 0) return Format.fill(many ? WORDS.trashMany : WORDS.trash, KeyName.keyFor("trash"), alone)
|
|
23
|
+
return Format.fill(many ? WORDS.trashManyAlone : WORDS.trashAlone, KeyName.keyFor("trash"))
|
|
24
|
+
}
|
|
25
|
+
function reviewPrompt() {
|
|
26
|
+
var alone = KeyName.keyOf("trash", "none")
|
|
27
|
+
return alone.length > 0 ? Format.fill(WORDS.review, KeyName.keyFor("trash"), alone) : Format.fill(WORDS.reviewAlone, KeyName.keyFor("trash"))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// The dd pair. A single d used to trash, and it sat in the middle of the letters a name is typed
|
|
31
|
+
// with, so the third keystroke of "Vid" trashed the row (issue 7). Delete and Ctrl+Delete are not
|
|
32
|
+
// letters and still go on one press, through Ops.trash directly.
|
|
33
|
+
|
|
34
|
+
// How long the first d stays armed. Long enough to be a deliberate pair, short enough that an arm
|
|
35
|
+
// nobody finished cannot be completed by a d typed minutes later.
|
|
36
|
+
var ARM_MS = 1500
|
|
37
|
+
|
|
38
|
+
// ui/js/Focus.js clears pane.trashArmedAt for every other action, so an arm never survives the key
|
|
39
|
+
// after it; this reads the stamp before it writes one, which is what makes the second press the pair.
|
|
40
|
+
function arm(pane) {
|
|
41
|
+
if (pane.trashArmedAt > 0 && Date.now() - pane.trashArmedAt < ARM_MS) {
|
|
42
|
+
pane.trashArmedAt = 0
|
|
43
|
+
Ops.trash(pane)
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
pane.trashArmedAt = Date.now()
|
|
47
|
+
pane.armPrompted(trashPrompt(Ops.targetIndices(pane).length))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Window-level keys the Trash view borrows: "direct" skips pane.act as Focus.handleKey does, "act" goes through it, "trash" stays here.
|
|
51
|
+
function route(action) {
|
|
52
|
+
if (action === "keymapSheet" || action === "pathBar") return "direct"
|
|
53
|
+
if (action.indexOf("textSize") === 0) return "direct"
|
|
54
|
+
if (action === "settings" || action === "sidebar") return "act"
|
|
55
|
+
if (action.indexOf("tab") === 0) return "act"
|
|
56
|
+
return "trash"
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The delete card's words come from a filled snapshot only (R0: the whole filled card or nothing), so an unfilled one says no word at all.
|
|
60
|
+
function filled(snapshot) {
|
|
61
|
+
return typeof snapshot.count === "number"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// "Empty Trash?", or one whole sentence per count; Format.fill takes the grouped count into the placeholder.
|
|
65
|
+
function confirmTitle(snapshot) {
|
|
66
|
+
if (!filled(snapshot)) return ""
|
|
67
|
+
if (snapshot.all) return "Empty Trash?"
|
|
68
|
+
return snapshot.count === 1 ? WORDS.deleteOne : Format.fill(WORDS.deleteMany, Format.count(snapshot.count))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function confirmBody(snapshot, scope) {
|
|
72
|
+
if (!filled(snapshot)) return ""
|
|
73
|
+
var body = snapshot.all ? Ops.deleteAllLine(snapshot.count, Format.size(snapshot.bytes || 0)) : Ops.deleteScopeLine(scope, snapshot.count)
|
|
74
|
+
if (snapshot.count === 1 && typeof snapshot.name === "string" && snapshot.name.length > 0)
|
|
75
|
+
body = "“" + snapshot.name + "”\n\n" + body
|
|
76
|
+
return body
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Where m opens: the cursor row's menu when no row is marked or the only mark is the cursor row, the marked rows' menu otherwise, the whole-Trash menu when nothing is listed.
|
|
80
|
+
function menuTarget(total, markedCount, cursorMarked) {
|
|
81
|
+
if (total === 0) return "strip"
|
|
82
|
+
if (markedCount === 0 || (markedCount === 1 && cursorMarked)) return "cursor"
|
|
83
|
+
return "marked"
|
|
84
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
function location(original, home) {
|
|
6
|
+
var cut = String(original).lastIndexOf("/")
|
|
7
|
+
if (cut < 0) return "Unknown"
|
|
8
|
+
var folder = cut === 0 ? "/" : original.slice(0, cut)
|
|
9
|
+
if (home && (folder === home || folder.indexOf(home + "/") === 0))
|
|
10
|
+
return "~" + folder.slice(home.length)
|
|
11
|
+
return folder
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Sample input, GIO trash::deletion-date: "2026-09-08T10:00:00" in local time.
|
|
15
|
+
function deleted(text) {
|
|
16
|
+
// The same guard expired() carries below, and for the same reason: new Date(null) is the epoch,
|
|
17
|
+
// so an item with no date would have been labelled with a day in 1970 instead of Unknown.
|
|
18
|
+
if (typeof text !== "string" || text.length === 0) return "Unknown"
|
|
19
|
+
var date = new Date(text)
|
|
20
|
+
if (!isFinite(date.getTime())) return "Unknown"
|
|
21
|
+
// Format.stamp is the product's one date form, so the trash queue sorts against the file list.
|
|
22
|
+
return Format.stamp(date)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The 30 day sweep's own two questions. Decided 2026-09-11: the sweep is off by default and
|
|
26
|
+
// opt in, because permanent deletion is outside the undo journal and nobody opted into it by
|
|
27
|
+
// installing an update.
|
|
28
|
+
var DAY_MS = 24 * 60 * 60 * 1000
|
|
29
|
+
|
|
30
|
+
// Sample input is deleted()'s own: gio's trash::deletion-date, "2026-09-08T10:00:00" in local time.
|
|
31
|
+
// An item whose date cannot be read is NEVER swept: this is the one direction a wrong answer here
|
|
32
|
+
// can be taken in, and "leave it alone" is that direction.
|
|
33
|
+
function expired(text, nowMs, days) {
|
|
34
|
+
// new Date(null) is the EPOCH, not an invalid date, so a missing value reads as 1970 and would
|
|
35
|
+
// be swept by every run. The type is checked before the parse rather than after it.
|
|
36
|
+
if (typeof text !== "string" || text.length === 0) return false
|
|
37
|
+
var date = new Date(text)
|
|
38
|
+
if (!isFinite(date.getTime())) return false
|
|
39
|
+
return nowMs - date.getTime() >= days * DAY_MS
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Which day it is, in whole days since the epoch at local midnight, which is what the sweep records
|
|
43
|
+
// so that it runs once a day rather than once per launch.
|
|
44
|
+
function dayNumber(nowMs) {
|
|
45
|
+
var now = new Date(nowMs)
|
|
46
|
+
return Math.floor(new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime() / DAY_MS)
|
|
47
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Drag.js" as Drag
|
|
4
|
+
.import "DragOut.js" as DragOut
|
|
5
|
+
.import "Ops.js" as Ops
|
|
6
|
+
.import "Swap.js" as Swap
|
|
7
|
+
.import "Mounts.js" as Mounts
|
|
8
|
+
|
|
9
|
+
// Only rows lifted in this window may trash through ui/RailTrashRow.qml, with dd's request and z's undo.
|
|
10
|
+
// Sample marker: "<instance>\n1,3\nmove\n%2Fd\n42\n1\n0\n7".
|
|
11
|
+
function markerRows(marker) {
|
|
12
|
+
var field = String(marker).split("\n")[1] || ""
|
|
13
|
+
if (field.length === 0) return []
|
|
14
|
+
var rows = []
|
|
15
|
+
var parts = field.split(",")
|
|
16
|
+
for (var i = 0; i < parts.length; i++) {
|
|
17
|
+
if (!/^[0-9]+$/.test(parts[i])) return []
|
|
18
|
+
var n = Number(parts[i])
|
|
19
|
+
if (!Number.isInteger(n) || n < 0) return []
|
|
20
|
+
rows.push(n)
|
|
21
|
+
}
|
|
22
|
+
return rows
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The request or null: carried paths survive listing changes; rows are only for a selection too wide to carry paths, on its source listing in lift-time numbering, so src/backend/rowguard.rs refuses stale indices.
|
|
26
|
+
function request(marker, urls, path, backend) {
|
|
27
|
+
if (!Drag.isOwnDrag(marker)) return null
|
|
28
|
+
// Issue 133: a GVFS mount has no trash, so the drop is refused up front as d and the menu are.
|
|
29
|
+
if (!Mounts.trashable(Drag.markerSource(marker) || path)) return null
|
|
30
|
+
var paths = DragOut.filePaths(urls)
|
|
31
|
+
if (paths.length > 0) return { c: "trash", paths: paths }
|
|
32
|
+
var rows = markerRows(marker)
|
|
33
|
+
var listing = Drag.markerListing(marker)
|
|
34
|
+
if (rows.length === 0 || listing <= 0 || !Drag.sameListing(marker, path) || !Drag.liftedBy(marker, backend)) return null
|
|
35
|
+
return Swap.named({ c: "trash", rows: rows }, listing)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Whether the drag can trash its carried files here, used by the hover check and status line.
|
|
39
|
+
function accepts(marker, urls, path, backend) {
|
|
40
|
+
return request(marker, urls, path, backend) !== null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Sends trash through the pane's backend and answers whether it sent; the trashed reply clears the selection and re-reads the listing.
|
|
44
|
+
function drop(pane, marker, urls) {
|
|
45
|
+
if (!pane || !pane.backend) return false
|
|
46
|
+
var sent = request(marker, urls, pane.path, pane.backend)
|
|
47
|
+
if (!sent) return false
|
|
48
|
+
// Where the cursor lands once the rows are gone, as dd sets it; only a drag from this listing has one.
|
|
49
|
+
var rows = Drag.sameListing(marker, pane.path) && Drag.liftedBy(marker, pane.backend) ? markerRows(marker) : []
|
|
50
|
+
var first = rows.length > 0 ? rows[0] : -1
|
|
51
|
+
for (var i = 1; i < rows.length; i++)
|
|
52
|
+
if (rows[i] < first) first = rows[i]
|
|
53
|
+
pane.trashedFirst = first
|
|
54
|
+
pane.backend.send(sent)
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The status bar while the drag rests on the Trash row. Sample: "Move 2 items to Trash".
|
|
59
|
+
function line(marker, urls, path, backend) {
|
|
60
|
+
if (!accepts(marker, urls, path, backend)) return ""
|
|
61
|
+
var rows = markerRows(marker)
|
|
62
|
+
var n = rows.length > 0 ? rows.length : DragOut.filePaths(urls).length
|
|
63
|
+
return "Move " + Ops.items(n) + " to Trash"
|
|
64
|
+
}
|