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/UiState.js
ADDED
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// ui/ViewState.qml's one-writer bookkeeping: saved/inflight/pending are patch bytes, pinned by tests/js/uistate.js.
|
|
4
|
+
|
|
5
|
+
// The window's own read of ui.json: an unreadable document stays as written and says so, while no file at all is a first launch.
|
|
6
|
+
function fromFile(text) {
|
|
7
|
+
try {
|
|
8
|
+
var found = JSON.parse(text)
|
|
9
|
+
if (found && typeof found === "object" && !Array.isArray(found)) {
|
|
10
|
+
return { state: found, unreadable: false }
|
|
11
|
+
}
|
|
12
|
+
} catch (e) {
|
|
13
|
+
// A hand edit this cannot parse, which is the ordinary way in and is not an error here.
|
|
14
|
+
}
|
|
15
|
+
return { state: {}, unreadable: text.length > 0 }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// The one sentence for a ui.json that could not be read when favorites were refreshed, said by this module and by ViewState.
|
|
19
|
+
var FAVOURITES_UNREAD = "Favorites could not be refreshed because ui.json could not be read. The old list stays."
|
|
20
|
+
|
|
21
|
+
// External favourites update independently of this window's settings drafts and pending patches.
|
|
22
|
+
function refreshedFavourites(state, text) {
|
|
23
|
+
var read = fromFile(text)
|
|
24
|
+
var places = read.state.places
|
|
25
|
+
if (text.length === 0 || read.unreadable || (places !== undefined && (!places || typeof places !== "object" || Array.isArray(places)))
|
|
26
|
+
|| (places && places.favourites !== undefined && !Array.isArray(places.favourites)))
|
|
27
|
+
return { state: state, error: FAVOURITES_UNREAD }
|
|
28
|
+
var records = places && places.favourites || []
|
|
29
|
+
if (JSON.stringify((state.places || {}).favourites || []) === JSON.stringify(records))
|
|
30
|
+
return { state: state, error: "" }
|
|
31
|
+
return { state: withGroup(state, "places", { favourites: records }), error: "" }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Compare a save with this window's intent, because the writer response can include concurrent edits.
|
|
35
|
+
function favouritesAfter(records, operation) {
|
|
36
|
+
var next = records.slice()
|
|
37
|
+
if (operation.op === "add") next.push(operation.record)
|
|
38
|
+
else if (operation.op === "remove") next.splice(operation.index, 1)
|
|
39
|
+
else if (operation.op === "move") next.splice(operation.to, 0, next.splice(operation.index, 1)[0])
|
|
40
|
+
else if (operation.op === "rename") next[operation.index] = Object.assign({}, next[operation.index], { label: operation.label })
|
|
41
|
+
return next
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Per-window keys name where a window is rather than how Flea behaves, so no window ever takes them from the file.
|
|
45
|
+
var WINDOW_KEYS = ["view", "pickerView", "columnWidths", "dual", "lastPath", "lastTabs",
|
|
46
|
+
"trashSweptOn", "stateVersion", "sort", "previewFacts"]
|
|
47
|
+
|
|
48
|
+
function isWindowKey(key) {
|
|
49
|
+
return WINDOW_KEYS.indexOf(key) >= 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Per-window leaves stay per window like the sidebar: a change still writes the file, but open windows keep their own.
|
|
53
|
+
var WINDOW_LEAF_KEYS = ["places.rail", "preview.column", "preview.thumbSize"]
|
|
54
|
+
|
|
55
|
+
function isWindowLeaf(key, leaf) {
|
|
56
|
+
return WINDOW_LEAF_KEYS.indexOf(key + "." + leaf) >= 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Sample patch: {"columns":["name","bogus"]} is refused whole while {"columns":["name","size"]} retries; only schema-invalid shapes prune.
|
|
60
|
+
function isPatchInvalid(patchText) {
|
|
61
|
+
return invalidKeys(patchText).length > 0
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Sample patch: {"columns":["name","bogus"],"density":"normal"} refuses ["columns"] alone, so a valid key beside it is never pruned.
|
|
65
|
+
function invalidKeys(patchText) {
|
|
66
|
+
var refused = []
|
|
67
|
+
var patch = null
|
|
68
|
+
try {
|
|
69
|
+
patch = JSON.parse(patchText)
|
|
70
|
+
} catch (e) {
|
|
71
|
+
return refused
|
|
72
|
+
}
|
|
73
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch))
|
|
74
|
+
return refused
|
|
75
|
+
if (patch.columns !== undefined) {
|
|
76
|
+
var cols = patch.columns
|
|
77
|
+
var bad = !Array.isArray(cols) || cols.indexOf("name") < 0
|
|
78
|
+
if (!bad) {
|
|
79
|
+
var seen = {}
|
|
80
|
+
var keys = ["name", "mode", "size", "date", "kind"]
|
|
81
|
+
for (var i = 0; i < cols.length; i++) {
|
|
82
|
+
if (keys.indexOf(cols[i]) < 0 || seen[cols[i]]) { bad = true; break }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (bad)
|
|
86
|
+
refused.push("columns")
|
|
87
|
+
}
|
|
88
|
+
if (patch.places !== undefined && (patch.places === null || typeof patch.places !== "object" || Array.isArray(patch.places)))
|
|
89
|
+
refused.push("places")
|
|
90
|
+
return refused
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// True when text parses as a JSON object; empty or garbage is ignored until the next valid write.
|
|
94
|
+
function parsesAsObject(text) {
|
|
95
|
+
try {
|
|
96
|
+
var found = JSON.parse(text)
|
|
97
|
+
return found && typeof found === "object" && !Array.isArray(found)
|
|
98
|
+
} catch (e) {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// A settler's exit and its collected text land in either order, so only a whole answer is ever read.
|
|
104
|
+
function landed(answer, half) {
|
|
105
|
+
return Object.assign({}, answer, half)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// An empty text is an answer too: a run that says nothing has still finished saying it.
|
|
109
|
+
function whole(answer) {
|
|
110
|
+
return answer.code !== undefined && answer.text !== undefined
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// The whole answer a settler that never started is given: no real exit status is negative.
|
|
114
|
+
var NEVER_RAN = { code: -1, text: "" }
|
|
115
|
+
|
|
116
|
+
// A settler that cannot start raises no exited, only running going false, and a real exit lands its status before that false.
|
|
117
|
+
function neverRan(answer, running) {
|
|
118
|
+
return !running && answer.code === undefined
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// A refused write queues its prune behind a running settle instead of hijacking that settle's mode.
|
|
122
|
+
function pruneAsk(running, failedPatch) {
|
|
123
|
+
return running ? { queue: failedPatch, start: "" } : { queue: "", start: failedPatch }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// A settle stays pending until both halves land, so one half landed still counts as running.
|
|
127
|
+
function settleBusy(running, mode, answer) {
|
|
128
|
+
return running || (mode.length > 0 && !whole(answer))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// A settle's end spends a queued prune before a dirty re-read, keeping the dirty flag across the prune so the re-read still runs after it.
|
|
132
|
+
function settleNext(dirty, pruneQueued) {
|
|
133
|
+
if (pruneQueued.length > 0)
|
|
134
|
+
return "prune"
|
|
135
|
+
if (dirty)
|
|
136
|
+
return "apply"
|
|
137
|
+
return "idle"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Drops only refused keys a retry can never land, keeping a valid key and a newer owed value the refused writer never carried.
|
|
141
|
+
function dropInvalid(unsaved, inflightText) {
|
|
142
|
+
var refused = invalidKeys(inflightText)
|
|
143
|
+
if (refused.length === 0)
|
|
144
|
+
return unsaved
|
|
145
|
+
var inflight = JSON.parse(inflightText)
|
|
146
|
+
var out = {}
|
|
147
|
+
for (var k in unsaved)
|
|
148
|
+
out[k] = unsaved[k]
|
|
149
|
+
for (var r = 0; r < refused.length; r++) {
|
|
150
|
+
var key = refused[r]
|
|
151
|
+
if (out[key] === undefined)
|
|
152
|
+
continue
|
|
153
|
+
if (isGroup(out[key]) && isGroup(inflight[key])) {
|
|
154
|
+
var kept = {}
|
|
155
|
+
var any = false
|
|
156
|
+
for (var leaf in out[key]) {
|
|
157
|
+
if (inflight[key][leaf] !== undefined
|
|
158
|
+
&& JSON.stringify(out[key][leaf]) === JSON.stringify(inflight[key][leaf]))
|
|
159
|
+
continue
|
|
160
|
+
kept[leaf] = out[key][leaf]
|
|
161
|
+
any = true
|
|
162
|
+
}
|
|
163
|
+
if (any)
|
|
164
|
+
out[key] = kept
|
|
165
|
+
else
|
|
166
|
+
delete out[key]
|
|
167
|
+
} else if (JSON.stringify(out[key]) === JSON.stringify(inflight[key])) {
|
|
168
|
+
delete out[key]
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return out
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Reverts only refused keys still showing the refused value, so a newer change made behind the refused writer survives.
|
|
175
|
+
function revertedState(state, inflightText, settled) {
|
|
176
|
+
var refused = invalidKeys(inflightText)
|
|
177
|
+
if (refused.length === 0)
|
|
178
|
+
return state
|
|
179
|
+
var inflight = null
|
|
180
|
+
try {
|
|
181
|
+
inflight = JSON.parse(inflightText)
|
|
182
|
+
} catch (e) {
|
|
183
|
+
return state
|
|
184
|
+
}
|
|
185
|
+
if (!inflight || typeof inflight !== "object" || !settled)
|
|
186
|
+
return state
|
|
187
|
+
var out = {}
|
|
188
|
+
for (var s in state)
|
|
189
|
+
out[s] = state[s]
|
|
190
|
+
for (var r = 0; r < refused.length; r++) {
|
|
191
|
+
var key = refused[r]
|
|
192
|
+
if (settled[key] === undefined || inflight[key] === undefined)
|
|
193
|
+
continue
|
|
194
|
+
if (isGroup(inflight[key]) && isGroup(settled[key]) && isGroup(out[key])) {
|
|
195
|
+
var group = {}
|
|
196
|
+
for (var h in out[key])
|
|
197
|
+
group[h] = out[key][h]
|
|
198
|
+
for (var leaf in inflight[key]) {
|
|
199
|
+
if (JSON.stringify(group[leaf]) !== JSON.stringify(inflight[key][leaf]))
|
|
200
|
+
continue
|
|
201
|
+
if (settled[key][leaf] !== undefined)
|
|
202
|
+
group[leaf] = settled[key][leaf]
|
|
203
|
+
else
|
|
204
|
+
delete group[leaf]
|
|
205
|
+
}
|
|
206
|
+
out[key] = group
|
|
207
|
+
} else if (JSON.stringify(out[key]) === JSON.stringify(inflight[key])) {
|
|
208
|
+
out[key] = settled[key]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return out
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// A change another window saved: preference keys the file names and this window does not owe take the file's value, never clobbering owed leaves or in-flight writes.
|
|
215
|
+
function applyExternal(state, unsaved, text) {
|
|
216
|
+
var read = fromFile(text)
|
|
217
|
+
if (read.unreadable)
|
|
218
|
+
return { state: state, changed: false }
|
|
219
|
+
var file = read.state
|
|
220
|
+
var out = {}
|
|
221
|
+
for (var s in state)
|
|
222
|
+
out[s] = state[s]
|
|
223
|
+
var changed = false
|
|
224
|
+
for (var key in file) {
|
|
225
|
+
if (isWindowKey(key))
|
|
226
|
+
continue
|
|
227
|
+
var value = file[key]
|
|
228
|
+
if (key === "places" && isGroup(value)) {
|
|
229
|
+
var merged = {}
|
|
230
|
+
for (var f in value) {
|
|
231
|
+
if (f !== "favourites")
|
|
232
|
+
merged[f] = value[f]
|
|
233
|
+
}
|
|
234
|
+
var held = state.places || {}
|
|
235
|
+
if (held.favourites !== undefined)
|
|
236
|
+
merged.favourites = held.favourites
|
|
237
|
+
var owedPlaces = unsaved ? unsaved.places : undefined
|
|
238
|
+
if (isGroup(owedPlaces)) {
|
|
239
|
+
for (var leaf in owedPlaces) {
|
|
240
|
+
if (leaf === "favourites")
|
|
241
|
+
continue
|
|
242
|
+
if (held[leaf] !== undefined)
|
|
243
|
+
merged[leaf] = held[leaf]
|
|
244
|
+
else
|
|
245
|
+
delete merged[leaf]
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (held.rail !== undefined)
|
|
249
|
+
merged.rail = held.rail
|
|
250
|
+
if (JSON.stringify(merged) !== JSON.stringify(state.places)) {
|
|
251
|
+
out.places = merged
|
|
252
|
+
changed = true
|
|
253
|
+
}
|
|
254
|
+
continue
|
|
255
|
+
}
|
|
256
|
+
var owed = unsaved ? unsaved[key] : undefined
|
|
257
|
+
if (owed !== undefined) {
|
|
258
|
+
if (isGroup(owed) && isGroup(value) && !isWholeKey(key)) {
|
|
259
|
+
var group = {}
|
|
260
|
+
for (var g in value)
|
|
261
|
+
group[g] = value[g]
|
|
262
|
+
var current = state[key] || {}
|
|
263
|
+
for (var o in owed) {
|
|
264
|
+
if (current[o] !== undefined)
|
|
265
|
+
group[o] = current[o]
|
|
266
|
+
else
|
|
267
|
+
delete group[o]
|
|
268
|
+
}
|
|
269
|
+
for (var w in group) {
|
|
270
|
+
if (isWindowLeaf(key, w)) {
|
|
271
|
+
if (current[w] !== undefined)
|
|
272
|
+
group[w] = current[w]
|
|
273
|
+
else
|
|
274
|
+
delete group[w]
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (JSON.stringify(group) !== JSON.stringify(state[key])) {
|
|
278
|
+
out[key] = group
|
|
279
|
+
changed = true
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
continue
|
|
283
|
+
}
|
|
284
|
+
if (isGroup(value) && isGroup(state[key])) {
|
|
285
|
+
var anyLeafWindow = false
|
|
286
|
+
for (var vl in value) {
|
|
287
|
+
if (isWindowLeaf(key, vl)) {
|
|
288
|
+
anyLeafWindow = true
|
|
289
|
+
break
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (anyLeafWindow) {
|
|
293
|
+
var leafGroup = {}
|
|
294
|
+
for (var vg in value) {
|
|
295
|
+
if (!isWindowLeaf(key, vg))
|
|
296
|
+
leafGroup[vg] = value[vg]
|
|
297
|
+
}
|
|
298
|
+
var heldGroup = state[key] || {}
|
|
299
|
+
for (var hg in heldGroup) {
|
|
300
|
+
if (isWindowLeaf(key, hg))
|
|
301
|
+
leafGroup[hg] = heldGroup[hg]
|
|
302
|
+
}
|
|
303
|
+
if (JSON.stringify(leafGroup) !== JSON.stringify(state[key])) {
|
|
304
|
+
out[key] = leafGroup
|
|
305
|
+
changed = true
|
|
306
|
+
}
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (JSON.stringify(value) !== JSON.stringify(state[key])) {
|
|
311
|
+
out[key] = value
|
|
312
|
+
changed = true
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { state: changed ? out : state, changed: changed }
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// A copy of the document with one top-level key replaced, and the nested version of the same. QML
|
|
319
|
+
// notifies on assignment and not on a mutation, so every writer rebuilds rather than reaching in;
|
|
320
|
+
// the nested one merges into the group beside it, because a whole-group assignment would take the
|
|
321
|
+
// half a writer holds as the whole of it. ui/ViewState.qml runs both over two documents at once:
|
|
322
|
+
// the state it draws from, and the patch it owes the state file.
|
|
323
|
+
function withKey(state, key, value) {
|
|
324
|
+
var out = {}
|
|
325
|
+
for (var s in state)
|
|
326
|
+
out[s] = state[s]
|
|
327
|
+
out[key] = value
|
|
328
|
+
return out
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function withGroup(state, key, next) {
|
|
332
|
+
var group = {}
|
|
333
|
+
var held = state[key] || {}
|
|
334
|
+
for (var h in held)
|
|
335
|
+
group[h] = held[h]
|
|
336
|
+
for (var n in next)
|
|
337
|
+
group[n] = next[n]
|
|
338
|
+
return withKey(state, key, group)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// The book a window starts with: nothing of its own written yet, and no writer running. Its own read
|
|
342
|
+
// of the file is not a patch it sent, so `saved` starts empty rather than holding what it read.
|
|
343
|
+
function book() {
|
|
344
|
+
return { saved: "", inflight: "", pending: "" }
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// A change asks for a write. The answer is the next book plus `start`, the patch to launch now.
|
|
348
|
+
function asked(b, patch) {
|
|
349
|
+
// The newest patch this window has landed or has on its way, so asking for exactly those bytes
|
|
350
|
+
// again sends nothing and a refused one is never short-circuited.
|
|
351
|
+
if (patch === (b.pending || b.inflight || b.saved)) {
|
|
352
|
+
return { saved: b.saved, inflight: b.inflight, pending: b.pending, start: "" }
|
|
353
|
+
}
|
|
354
|
+
// One writer at a time, and the newest patch waits rather than being dropped on the floor.
|
|
355
|
+
if (b.inflight.length > 0) {
|
|
356
|
+
return { saved: b.saved, inflight: b.inflight, pending: patch, start: "" }
|
|
357
|
+
}
|
|
358
|
+
return { saved: b.saved, inflight: patch, pending: "", start: patch }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// The writer exited. The answer is the next book plus `start`, and `failed` for the pane to report.
|
|
362
|
+
// `owed` is what the window still owes NOW and is what a queued writer launches with, because the
|
|
363
|
+
// bytes waiting in `pending` were built before this writer landed: they still name the settings it
|
|
364
|
+
// just stored, and re-sending one writes this window's own copy of it over whatever another window
|
|
365
|
+
// or the CLI put there in between. A refusal changes nothing, so there `owed` is those same bytes.
|
|
366
|
+
function exited(b, code, owed) {
|
|
367
|
+
// Nothing waiting, or nothing left owed once this writer's own settings came out of it, which a
|
|
368
|
+
// value changed and changed back under one writer produces: an empty patch is a process and a
|
|
369
|
+
// rename spent on a document that would come out byte for byte the same.
|
|
370
|
+
var next = (b.pending.length > 0 && owed !== "{}") ? owed : ""
|
|
371
|
+
return {
|
|
372
|
+
// Only a zero status proves the patch reached the file: src/main.rs exits 2 on a refused
|
|
373
|
+
// patch and on a state directory it could not write, and the change is on screen either way.
|
|
374
|
+
saved: code === 0 ? b.inflight : b.saved,
|
|
375
|
+
inflight: next,
|
|
376
|
+
pending: "",
|
|
377
|
+
start: next,
|
|
378
|
+
failed: code !== 0
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// What is still owed once the patch a writer landed is taken out of it. A setting is only cleared
|
|
383
|
+
// when what the window owes for it now is what that writer carried: a change made while the writer
|
|
384
|
+
// ran is a newer value for the same setting, and the file does not have that one yet.
|
|
385
|
+
function acknowledged(unsaved, patch) {
|
|
386
|
+
var landed
|
|
387
|
+
try {
|
|
388
|
+
landed = JSON.parse(patch)
|
|
389
|
+
} catch (e) {
|
|
390
|
+
// Bytes this file built itself, so a parse failure clears nothing rather than clearing wrong.
|
|
391
|
+
return unsaved
|
|
392
|
+
}
|
|
393
|
+
if (!landed || typeof landed !== "object" || Array.isArray(landed))
|
|
394
|
+
return unsaved
|
|
395
|
+
var out = {}
|
|
396
|
+
for (var key in unsaved) {
|
|
397
|
+
var still = stillOwed(unsaved[key], landed[key], key)
|
|
398
|
+
if (still !== undefined)
|
|
399
|
+
out[key] = still
|
|
400
|
+
}
|
|
401
|
+
return out
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// One key of the owed patch against the same key of the landed one: `undefined` when the writer took
|
|
405
|
+
// all of it, and otherwise what is left. Two objects are a settings group and are walked leaf by
|
|
406
|
+
// leaf, because changeLeaf owes the leaf alone and clearing the group would drop a leaf beside it
|
|
407
|
+
// that no writer has taken yet. A map (changeMapEntries) owes entry by entry, which is the same
|
|
408
|
+
// walk. A whole-value object key below is the exception: it is compared and cleared whole, because
|
|
409
|
+
// src/uischema.rs Rule::LastTabs stands or falls together and a half value is refused whole.
|
|
410
|
+
function stillOwed(owed, landed, key) {
|
|
411
|
+
if (landed === undefined)
|
|
412
|
+
return owed
|
|
413
|
+
if (isWholeKey(key) && isGroup(owed) && isGroup(landed))
|
|
414
|
+
return JSON.stringify(owed) === JSON.stringify(landed) ? undefined : owed
|
|
415
|
+
if (isGroup(owed) && isGroup(landed)) {
|
|
416
|
+
var kept = {}
|
|
417
|
+
var any = false
|
|
418
|
+
for (var leaf in owed) {
|
|
419
|
+
if (JSON.stringify(owed[leaf]) === JSON.stringify(landed[leaf]))
|
|
420
|
+
continue
|
|
421
|
+
kept[leaf] = owed[leaf]
|
|
422
|
+
any = true
|
|
423
|
+
}
|
|
424
|
+
return any ? kept : undefined
|
|
425
|
+
}
|
|
426
|
+
return JSON.stringify(owed) === JSON.stringify(landed) ? undefined : owed
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// A settings group, which is the only shape withGroup builds: an array is a whole key's value.
|
|
430
|
+
function isGroup(value) {
|
|
431
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Whole-value object keys: owed and cleared whole, never leaf by leaf. Today only lastTabs, whose
|
|
435
|
+
// src/uischema.rs Rule::LastTabs takes exactly paths plus index together.
|
|
436
|
+
function isWholeKey(key) {
|
|
437
|
+
return key === "lastTabs"
|
|
438
|
+
}
|
package/ui/js/Unlock.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "Devices.js" as Devices
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// Unlock (U039): the dialog's sentences and the udisks answers it parses; a passphrase goes only to the child's stdin.
|
|
6
|
+
|
|
7
|
+
// Whole sentences with "%1" for the drive's label, filled by Format.fill.
|
|
8
|
+
var WORDS = {
|
|
9
|
+
title: "Unlock %1",
|
|
10
|
+
unlocking: "Unlocking %1…",
|
|
11
|
+
wrong: "That passphrase did not unlock %1.",
|
|
12
|
+
unlocked: "Unlocked and mounted %1.",
|
|
13
|
+
lockFailed: "%1 was unmounted but could not be locked; close what is using it.",
|
|
14
|
+
mountFailed: "%1 was unlocked but could not be mounted.",
|
|
15
|
+
unlockFailed: "%1 could not be unlocked."
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Sample input: the label of the locked rail row the dialog opened over.
|
|
19
|
+
function title(label) {
|
|
20
|
+
return Format.fill(WORDS.title, label || "")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Sample input: "Unlocked /dev/sda1 as /dev/dm-0.", the line udisksctl unlock prints.
|
|
24
|
+
function parseUnlocked(output) {
|
|
25
|
+
var m = String(output || "").match(/as\s+(\/dev\/\S+)/)
|
|
26
|
+
return m ? m[1].replace(/[.,;:\s]+$/, "") : ""
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Sample input: "Backup", the locked rail row's own label.
|
|
30
|
+
function unlockingNote(label) {
|
|
31
|
+
return Format.fill(WORDS.unlocking, label || "")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Sample input: "Backup", the drive the passphrase did not open.
|
|
35
|
+
function wrongNote(label) {
|
|
36
|
+
return Format.fill(WORDS.wrong, label || "")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Sample input: "Backup", the drive just unlocked and mounted.
|
|
40
|
+
function unlockedNote(label) {
|
|
41
|
+
return Format.fill(WORDS.unlocked, label || "")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Sample input: "Backup", the drive unmounted but still unlocked.
|
|
45
|
+
function lockNote(label) {
|
|
46
|
+
return Format.fill(WORDS.lockFailed, label || "")
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Sample: "Mounted /dev/dm-0 at /run/media/gm/My Backup." ends with a period.
|
|
50
|
+
function parseMounted(output) {
|
|
51
|
+
var m = String(output || "").match(/\bat\s+(\/.*?)\.?$/m)
|
|
52
|
+
return m ? m[1] : ""
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Sample input: "Backup", the drive unlocked but never mounted.
|
|
56
|
+
function mountNote(label) {
|
|
57
|
+
return Format.fill(WORDS.mountFailed, label || "")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Sample input: "Backup", the drive whose unlock failed for a reason other than the passphrase.
|
|
61
|
+
function unlockFailedNote(label) {
|
|
62
|
+
return Format.fill(WORDS.unlockFailed, label || "")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Sample input: "Error unlocking /dev/sdb1: GDBus.Error:org.freedesktop.UDisks2.Error.Failed: x", answered with the error name.
|
|
66
|
+
function parseDbusError(output) {
|
|
67
|
+
var m = String(output || "").match(/GDBus\.Error:([A-Za-z0-9._-]+)/)
|
|
68
|
+
return m ? m[1] : ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Sample input: "/dev/sdb1"; udisksctl opens --key-file as a path, so "-" names a file and stdin is /dev/stdin.
|
|
72
|
+
function unlockArgv(device) {
|
|
73
|
+
return ["udisksctl", "unlock", "--block-device", String(device || ""), "--key-file", "/dev/stdin"]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Sample input: "Error unlocking /dev/sda1: GDBus.Error:org.freedesktop.UDisks2.Error.Failed: Incorrect passphrase." reads as a wrong passphrase; only the error name decides, so every Failed does, and any other name (NotAuthorized among them) reads as could not be unlocked.
|
|
77
|
+
function refusalNote(stderr, label) {
|
|
78
|
+
return parseDbusError(stderr) === "org.freedesktop.UDisks2.Error.Failed" ? wrongNote(label) : unlockFailedNote(label)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Sample input: the rail's device rows and "/dev/loop0"; the locked row for that device, or null before the rail lists it.
|
|
82
|
+
function lockedRow(entries, device) {
|
|
83
|
+
var rows = entries || []
|
|
84
|
+
for (var i = 0; i < rows.length; i++) {
|
|
85
|
+
if (rows[i] && rows[i].locked === true && String(rows[i].device || "") === String(device || ""))
|
|
86
|
+
return rows[i]
|
|
87
|
+
}
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Sample input: [], one locked fallback row and "/dev/loop0"; the locked row wherever the list lives, rail first.
|
|
92
|
+
function namingRow(primary, fallback, device) {
|
|
93
|
+
return lockedRow(primary, device) || lockedRow(fallback, device)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Sample input: "crypto_LUKS" with "2", the fstype and FSVER lsblk reports for a LUKS2 container.
|
|
97
|
+
function cryptoName(fstype, fsver) {
|
|
98
|
+
if (String(fstype || "").toLowerCase() === "crypto_luks") {
|
|
99
|
+
if (String(fsver || "") === "2")
|
|
100
|
+
return "LUKS2"
|
|
101
|
+
if (String(fsver || "") === "1")
|
|
102
|
+
return "LUKS1"
|
|
103
|
+
return "LUKS"
|
|
104
|
+
}
|
|
105
|
+
var name = String(fstype || "").replace(/^crypto_/i, "").toUpperCase()
|
|
106
|
+
return name.length > 0 ? name : "LUKS"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Sample input: "Backup", "500.0 GB", "LUKS2", the name row under the title, one template per shape.
|
|
110
|
+
function subtitle(label, sizeText, crypto) {
|
|
111
|
+
var name = String(label || "")
|
|
112
|
+
var size = String(sizeText || "")
|
|
113
|
+
var kind = String(crypto || "")
|
|
114
|
+
if (size.length > 0 && kind.length > 0)
|
|
115
|
+
return "%1 · %2 · %3".arg(name).arg(size).arg(kind)
|
|
116
|
+
if (size.length > 0)
|
|
117
|
+
return "%1 · %2".arg(name).arg(size)
|
|
118
|
+
if (kind.length > 0)
|
|
119
|
+
return "%1 · %2".arg(name).arg(kind)
|
|
120
|
+
return name
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Sample input: the lsblk body and "/dev/mapper/luks-vault", answered with its container "/dev/sdb1", or "" when gone.
|
|
124
|
+
function backingFor(body, cryptDevice) {
|
|
125
|
+
var tree
|
|
126
|
+
try {
|
|
127
|
+
tree = JSON.parse(String(body || ""))
|
|
128
|
+
} catch (e) {
|
|
129
|
+
return ""
|
|
130
|
+
}
|
|
131
|
+
var want = String(cryptDevice || "")
|
|
132
|
+
var nodes = (tree && tree.blockdevices) || []
|
|
133
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
134
|
+
var found = backingIn(nodes[i], want)
|
|
135
|
+
if (found.length > 0)
|
|
136
|
+
return found
|
|
137
|
+
}
|
|
138
|
+
return ""
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// A crypt leaf's parent is the container udisksctl lock takes, whatever depth it sits at; no other type has one.
|
|
142
|
+
function backingIn(node, want) {
|
|
143
|
+
var kids = node.children || []
|
|
144
|
+
for (var i = 0; i < kids.length; i++) {
|
|
145
|
+
if (String(kids[i].type || "") === "crypt" && Devices.devicePath(kids[i]) === want)
|
|
146
|
+
return Devices.devicePath(node)
|
|
147
|
+
var deeper = backingIn(kids[i], want)
|
|
148
|
+
if (deeper.length > 0)
|
|
149
|
+
return deeper
|
|
150
|
+
}
|
|
151
|
+
return ""
|
|
152
|
+
}
|