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/src/compare.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Compare uses the same bunmsh -cc builtin launch convention as Find.
|
|
2
|
+
import {existsSync} from 'node:fs';
|
|
3
|
+
import {open} from 'node:fs/promises';
|
|
4
|
+
import {constants} from 'node:fs';
|
|
5
|
+
import {join,isAbsolute} from 'node:path';
|
|
6
|
+
const LIMIT=1024*1024, PATCH_LIMIT=8*LIMIT;
|
|
7
|
+
export function diffCommands({which=Bun.which,exists=existsSync,beside=join(import.meta.dir,'../../bunmsh/index.js')}={}) {
|
|
8
|
+
const commands=[];
|
|
9
|
+
if(exists(beside))commands.push([which('bun')||process.argv0,beside,'-cc','builtin','diff']);
|
|
10
|
+
const shell=which('bunmsh');if(shell)commands.push([shell,'-cc','builtin','diff']);
|
|
11
|
+
const diff=which('diff');if(diff)commands.push([diff]);
|
|
12
|
+
return commands;
|
|
13
|
+
}
|
|
14
|
+
async function read(path) {
|
|
15
|
+
if(typeof path!=='string'||!isAbsolute(path))throw Error('Not an absolute path');
|
|
16
|
+
const f=await open(path,constants.O_RDONLY|(constants.O_NONBLOCK||0));
|
|
17
|
+
try {
|
|
18
|
+
const stat=await f.stat();if(!stat.isFile())throw Error('Not a regular file');
|
|
19
|
+
if(stat.size>LIMIT)return {over:true};
|
|
20
|
+
const data=Buffer.alloc(LIMIT+1);let count=0;
|
|
21
|
+
while(count<data.length){const r=await f.read(data,count,data.length-count,null);if(!r.bytesRead)break;count+=r.bytesRead;}
|
|
22
|
+
return {over:count>LIMIT,data:data.subarray(0,count)};
|
|
23
|
+
}finally{await f.close()}
|
|
24
|
+
}
|
|
25
|
+
async function run(argv,signal,timeout) {
|
|
26
|
+
let child,reader,timer,stopped=false;
|
|
27
|
+
// Diff is read-only: cancellation needs no graceful shutdown. Terminate even a
|
|
28
|
+
// provider that ignores SIGTERM, and release a pipe inherited by descendants.
|
|
29
|
+
const stop=()=>{
|
|
30
|
+
if(stopped)return;
|
|
31
|
+
stopped=true;
|
|
32
|
+
try{child?.kill('SIGKILL')}catch{}
|
|
33
|
+
if(reader)void reader.cancel().catch(()=>{});
|
|
34
|
+
};
|
|
35
|
+
try {
|
|
36
|
+
if(signal?.aborted)return null;
|
|
37
|
+
child=Bun.spawn(argv,{stdin:'ignore',stdout:'pipe',stderr:'ignore',env:{...process.env,NO_COLOR:'1',FORCE_COLOR:'0'}});
|
|
38
|
+
reader=child.stdout.getReader();
|
|
39
|
+
signal?.addEventListener('abort',stop,{once:true});
|
|
40
|
+
if(signal?.aborted)stop();
|
|
41
|
+
timer=setTimeout(stop,timeout);
|
|
42
|
+
const chunks=[];let size=0;
|
|
43
|
+
while(!stopped){
|
|
44
|
+
const {done,value:chunk}=await reader.read();if(done)break;
|
|
45
|
+
size+=chunk.length;if(size>PATCH_LIMIT){stop();break;}chunks.push(chunk);
|
|
46
|
+
}
|
|
47
|
+
const code=await child.exited;if(stopped)return null;
|
|
48
|
+
const patch=new TextDecoder('utf-8',{fatal:true}).decode(Buffer.concat(chunks));
|
|
49
|
+
if(code===0)return {state:'identical',patch:''};
|
|
50
|
+
if(code===1 && /^--- .*\n\+\+\+ .*\n@@ /s.test(patch))return {state:'diff',patch};
|
|
51
|
+
return false; // An older bunmsh may not yet support -u: try the next provider.
|
|
52
|
+
}catch{return stopped?null:false}
|
|
53
|
+
finally{
|
|
54
|
+
clearTimeout(timer);signal?.removeEventListener('abort',stop);
|
|
55
|
+
if(reader){try{await reader.cancel()}catch{}reader.releaseLock();}
|
|
56
|
+
if(child){try{child.kill('SIGKILL')}catch{}await child.exited.catch(()=>{});}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export async function compare(request,{commands=diffCommands(),signal,timeout=10000}={}) {
|
|
60
|
+
const reply={t:'compare',token:request.token,state:'failed',patch:''};
|
|
61
|
+
try {
|
|
62
|
+
const a=await read(request.a),b=await read(request.b);
|
|
63
|
+
if(a.over||b.over)return {...reply,state:'over'};
|
|
64
|
+
if(a.data.subarray(0,8192).includes(0)||b.data.subarray(0,8192).includes(0))return {...reply,state:'binary'};
|
|
65
|
+
const decoder=new TextDecoder('utf-8',{fatal:true});decoder.decode(a.data);decoder.decode(b.data);
|
|
66
|
+
for(const prefix of commands){
|
|
67
|
+
if(signal?.aborted)return null;
|
|
68
|
+
const result=await run([...prefix,'-u','--label',request.a,'--label',request.b,'--',request.a,request.b],signal,timeout);
|
|
69
|
+
if(result)return {...reply,...result};
|
|
70
|
+
if(result===null)break;
|
|
71
|
+
}
|
|
72
|
+
}catch{}
|
|
73
|
+
return signal?.aborted?null:reply;
|
|
74
|
+
}
|
package/src/create.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Making things: `mkdir`, the menu's `newfile` and `duplicate` (docs/protocol.md "mkdir",
|
|
2
|
+
// "duplicate"). Upstream: src/backend/opsdispatch.rs, menu_actions.rs `create_file`, ops.rs.
|
|
3
|
+
// Not yet: the undo journal, so nothing made here can be undone.
|
|
4
|
+
import { lstatSync, mkdirSync, openSync, closeSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, join, isAbsolute } from "node:path";
|
|
6
|
+
import { capturedPaths } from "./menu.js";
|
|
7
|
+
import { copyName, copyTree, reason } from "./transfer.js";
|
|
8
|
+
|
|
9
|
+
const NAME_TRIES = 1000;
|
|
10
|
+
const slash = (path) => (process.platform === "win32" ? path.replaceAll("\\", "/") : path);
|
|
11
|
+
|
|
12
|
+
/** A bare name in one folder: not empty, not `.` or `..`, no separator and no NUL. */
|
|
13
|
+
export function validName(name) {
|
|
14
|
+
return typeof name === "string" && name !== "" && name !== "." && name !== ".." && !/[/\0]/.test(name)
|
|
15
|
+
&& !(process.platform === "win32" && name.includes("\\"));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const exists = (path) => {
|
|
19
|
+
try {
|
|
20
|
+
lstatSync(path);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// The mkdir sentences of src/error.rs, which the client words as they come.
|
|
28
|
+
const mkdirReason = (e) =>
|
|
29
|
+
({
|
|
30
|
+
EEXIST: "a folder or file with that name already exists",
|
|
31
|
+
ENOENT: "item not found",
|
|
32
|
+
ENOTDIR: "item not found",
|
|
33
|
+
EACCES: "permission denied",
|
|
34
|
+
EPERM: "permission denied",
|
|
35
|
+
EROFS: "the drive is read-only",
|
|
36
|
+
ENAMETOOLONG: "that name is too long",
|
|
37
|
+
ENOSPC: "the drive is full",
|
|
38
|
+
})[e?.code] ?? String(e?.message ?? e);
|
|
39
|
+
|
|
40
|
+
// An opted-in `request` adds itself to the success line and sends the creation receipt after it.
|
|
41
|
+
function receipt(send, request, kind, fields) {
|
|
42
|
+
const id = request.request;
|
|
43
|
+
if (!Number.isSafeInteger(id) || id <= 0) return;
|
|
44
|
+
send({ t: "menuaction", op: "creation", id, kind, ...fields });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function mkdir(request, send) {
|
|
48
|
+
const parent = request.path;
|
|
49
|
+
const fail = (msg) => {
|
|
50
|
+
send({ t: "error", where: "mkdir", path: String(parent ?? ""), msg });
|
|
51
|
+
receipt(send, request, "made", { ok: false, error: msg });
|
|
52
|
+
};
|
|
53
|
+
if (typeof parent !== "string" || !isAbsolute(parent)) return fail("the folder is not an absolute path");
|
|
54
|
+
const typed = typeof request.name === "string" && request.name !== "";
|
|
55
|
+
if (typed && !validName(request.name)) return fail("that name is not allowed");
|
|
56
|
+
// A typed name is never renumbered; no name takes the first free New Folder.
|
|
57
|
+
for (let n = 1; n <= (typed ? 1 : NAME_TRIES); n++) {
|
|
58
|
+
const name = typed ? request.name : n === 1 ? "New Folder" : `New Folder ${n}`,
|
|
59
|
+
path = join(parent, name);
|
|
60
|
+
try {
|
|
61
|
+
mkdirSync(path);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e?.code === "EEXIST" && !typed) continue;
|
|
64
|
+
return fail(mkdirReason(e));
|
|
65
|
+
}
|
|
66
|
+
const id = request.request;
|
|
67
|
+
send({ t: "made", ok: true, path: slash(path), ...(Number.isSafeInteger(id) && id > 0 ? { request: id } : {}) });
|
|
68
|
+
receipt(send, request, "made", { ok: true, path: slash(path) });
|
|
69
|
+
send({ t: "changed", path: slash(parent) });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
fail("every New Folder name is already taken");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// The New file card: one empty file, created exclusively, so neither a file nor a dangling link
|
|
76
|
+
// already at that name is ever opened.
|
|
77
|
+
export function newfile(request, send) {
|
|
78
|
+
const answer = (fields) => send({ t: "menuaction", id: request.id ?? 0, op: "newFile", ...fields });
|
|
79
|
+
const parent = request.path,
|
|
80
|
+
name = request.name;
|
|
81
|
+
if (typeof parent !== "string" || !isAbsolute(parent) || !validName(name))
|
|
82
|
+
return answer({ ok: false, error: "New File requires an absolute folder and one non-empty filename." });
|
|
83
|
+
const path = join(parent, name);
|
|
84
|
+
try {
|
|
85
|
+
closeSync(openSync(path, "wx", 0o666));
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return answer({ ok: false, error: `Could not create ${path}: ${reason(e)}.` });
|
|
88
|
+
}
|
|
89
|
+
answer({ ok: true, path: slash(path) });
|
|
90
|
+
send({ t: "changed", path: slash(parent) });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Copies one path to its first free " copy" sibling; a folder duplicates its whole tree.
|
|
94
|
+
let duplicating = Promise.resolve();
|
|
95
|
+
export function duplicate(request, send) {
|
|
96
|
+
const run = () => duplicateOne(request, send);
|
|
97
|
+
duplicating = duplicating.then(run, run);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function duplicateOne(request, send) {
|
|
101
|
+
const source = request.path;
|
|
102
|
+
const fail = (msg) => {
|
|
103
|
+
send({ t: "error", where: "duplicate", path: String(source ?? ""), msg });
|
|
104
|
+
receipt(send, request, "duplicated", { ok: false, error: msg });
|
|
105
|
+
};
|
|
106
|
+
if (typeof source !== "string" || !isAbsolute(source)) return fail("the path is not absolute");
|
|
107
|
+
if (request.menuId && !capturedPaths(request.menuId)?.includes(source)) return fail("Selected item changed; reopen the menu.");
|
|
108
|
+
if (!exists(source)) return fail("it no longer exists");
|
|
109
|
+
const folder = dirname(source),
|
|
110
|
+
name = basename(source);
|
|
111
|
+
let target = null;
|
|
112
|
+
for (let n = 1; n <= NAME_TRIES && !target; n++) {
|
|
113
|
+
const candidate = join(folder, copyName(name, n));
|
|
114
|
+
if (!exists(candidate)) target = candidate;
|
|
115
|
+
}
|
|
116
|
+
if (!target) return fail("every copy name for this item is already taken");
|
|
117
|
+
try {
|
|
118
|
+
await copyTree(source, target);
|
|
119
|
+
} catch (e) {
|
|
120
|
+
// A copy that failed partway leaves its partial sibling, as upstream does, and the folder shows it.
|
|
121
|
+
send({ t: "changed", path: slash(folder) });
|
|
122
|
+
return fail(e?.sentence ?? reason(e));
|
|
123
|
+
}
|
|
124
|
+
const id = request.request;
|
|
125
|
+
send({ t: "duplicated", ok: true, path: slash(target), ...(Number.isSafeInteger(id) && id > 0 ? { request: id } : {}) });
|
|
126
|
+
receipt(send, request, "duplicated", { ok: true, path: slash(target) });
|
|
127
|
+
send({ t: "changed", path: slash(folder) });
|
|
128
|
+
}
|
package/src/dirsize.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Folder sizes, only when the user asks (docs/protocol.md "dirsize"): a click on a folder's ⚖️ Size
|
|
2
|
+
// cell asks for that row, the ⚖️ on the Size header asks for every folder in the listing. Nothing
|
|
3
|
+
// here ever starts a walk on its own: no viewport sweep, no size sort, no refresh.
|
|
4
|
+
//
|
|
5
|
+
// The walk is `du -s -b -x -0`, streamed: bunmsh's du beside this repository or on PATH, else the
|
|
6
|
+
// system's (src/find.js duCommand). -b is apparent bytes, the unit file rows already show; -x stays
|
|
7
|
+
// on the folder's own filesystem as upstream's walker does; each folder's line arrives as soon as
|
|
8
|
+
// it is finished, so sizes land one by one. One du runs at a time; rows asked while it runs wait
|
|
9
|
+
// for the next one. Several folders share one run, so a file hard-linked into two of them counts
|
|
10
|
+
// in the first only, as `du -s a b` counts it.
|
|
11
|
+
//
|
|
12
|
+
// Answers are kept by full path and handed out again on later rows (`z`, and `zp` when partial)
|
|
13
|
+
// after a sort, a refresh or a return to the folder, never recomputed. A `changed` line for a
|
|
14
|
+
// folder forgets that folder and every folder above it, whose totals it is part of, and nothing
|
|
15
|
+
// else: a busy folder never touches the sizes of its siblings. A change deep inside a measured
|
|
16
|
+
// folder that no watch sees leaves its size as it was until the user measures it again.
|
|
17
|
+
import { resolve, dirname } from "node:path";
|
|
18
|
+
import { duCommand } from "./find.js";
|
|
19
|
+
|
|
20
|
+
const known = new Map(),
|
|
21
|
+
waiting = new Set();
|
|
22
|
+
let job = null,
|
|
23
|
+
send = () => {},
|
|
24
|
+
current = () => null;
|
|
25
|
+
|
|
26
|
+
export function dirsizeInit(sender, listingOf) {
|
|
27
|
+
send = sender;
|
|
28
|
+
current = listingOf;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The kept size of the folder at `path`, or undefined. */
|
|
32
|
+
export const keptSize = (path) => known.get(resolve(path));
|
|
33
|
+
|
|
34
|
+
/** A folder changed: its own size and every one above it are no longer known. */
|
|
35
|
+
export function forgetSizes(path) {
|
|
36
|
+
if (!known.size || typeof path !== "string") return;
|
|
37
|
+
let at = resolve(path);
|
|
38
|
+
for (;;) {
|
|
39
|
+
known.delete(at);
|
|
40
|
+
const up = dirname(at);
|
|
41
|
+
if (up === at) break;
|
|
42
|
+
at = up;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The row a measured path holds in the listing now, which a sort since the ask may have moved.
|
|
47
|
+
function rowOf(full) {
|
|
48
|
+
const listing = current();
|
|
49
|
+
if (!listing || listing.trash) return -1;
|
|
50
|
+
const base = resolve(listing.path || ".");
|
|
51
|
+
return listing.rows.findIndex((row) => row.dir && resolve(base, row.name) === full);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `dirsize`: { rows: [...] } or { all: true }; answers dirsized per folder, then one dirsizedone. */
|
|
55
|
+
export function dirsize(request) {
|
|
56
|
+
const listing = current();
|
|
57
|
+
if (listing && !listing.trash) {
|
|
58
|
+
const base = resolve(listing.path || ".");
|
|
59
|
+
const indices = request.all === true ? listing.rows.map((_, i) => i)
|
|
60
|
+
: Array.isArray(request.rows) ? request.rows.filter(Number.isInteger) : [];
|
|
61
|
+
for (const i of indices) {
|
|
62
|
+
const row = listing.rows[i];
|
|
63
|
+
if (row?.dir) waiting.add(resolve(base, row.name));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!job) run();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** `dirsizecancel`: the run in flight stops and nothing waits any more. */
|
|
70
|
+
export function dirsizeCancel() {
|
|
71
|
+
waiting.clear();
|
|
72
|
+
if (!job) return send({ t: "dirsizedone", cancelled: true });
|
|
73
|
+
job.cancelled = true;
|
|
74
|
+
job.child?.kill();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const quotedNames = (errors) => {
|
|
78
|
+
// GNU and bunmsh both quote the path: "du: cannot read directory './photos/2019': Permission denied".
|
|
79
|
+
const names = new Set();
|
|
80
|
+
for (const match of errors.matchAll(/'\.[\\/]([^\\/']+)/g)) names.add(match[1]);
|
|
81
|
+
return names;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// The waiting folders that share the first one's parent: du runs there with "./name" operands, so a
|
|
85
|
+
// name starting with "-" stays a name and each printed name maps straight back.
|
|
86
|
+
function run() {
|
|
87
|
+
if (!waiting.size) return send({ t: "dirsizedone", cancelled: false });
|
|
88
|
+
const command = duCommand();
|
|
89
|
+
if (!command) {
|
|
90
|
+
waiting.clear();
|
|
91
|
+
send({ t: "error", where: "dirsize", path: "", msg: "no du could be found to measure folders" });
|
|
92
|
+
return send({ t: "dirsizedone", cancelled: false });
|
|
93
|
+
}
|
|
94
|
+
const parent = dirname(waiting.values().next().value);
|
|
95
|
+
const here = [...waiting].filter((full) => dirname(full) === parent);
|
|
96
|
+
for (const full of here) waiting.delete(full);
|
|
97
|
+
const names = here.map((full) => "./" + full.slice(parent.length).replace(/^[\\/]+/, ""));
|
|
98
|
+
const started = performance.now();
|
|
99
|
+
const mine = (job = { cancelled: false, child: null });
|
|
100
|
+
let child;
|
|
101
|
+
try {
|
|
102
|
+
child = Bun.spawn([...command, "-s", "-b", "-x", "-0", "--", ...names], { cwd: parent, stdin: "ignore", stdout: "pipe", stderr: "pipe" });
|
|
103
|
+
} catch (e) {
|
|
104
|
+
job = null;
|
|
105
|
+
send({ t: "error", where: "dirsize", path: parent, msg: String(e?.message ?? e) });
|
|
106
|
+
return run();
|
|
107
|
+
}
|
|
108
|
+
mine.child = child;
|
|
109
|
+
let errors = "";
|
|
110
|
+
const answered = new Map();
|
|
111
|
+
const answer = (name, bytes, partial) => {
|
|
112
|
+
const full = resolve(parent, name);
|
|
113
|
+
known.set(full, { bytes, partial });
|
|
114
|
+
answered.set(name, bytes);
|
|
115
|
+
if (mine.cancelled) return;
|
|
116
|
+
const row = rowOf(full);
|
|
117
|
+
if (row >= 0) send({ t: "dirsized", row, bytes, partial, ms: Number((performance.now() - started).toFixed(3)) });
|
|
118
|
+
};
|
|
119
|
+
const errorsDone = (async () => {
|
|
120
|
+
for await (const chunk of child.stderr) errors += Buffer.from(chunk).toString();
|
|
121
|
+
})();
|
|
122
|
+
const stdoutDone = (async () => {
|
|
123
|
+
let rest = "";
|
|
124
|
+
const decoder = new TextDecoder();
|
|
125
|
+
for await (const chunk of child.stdout) {
|
|
126
|
+
rest += decoder.decode(chunk, { stream: true });
|
|
127
|
+
let at;
|
|
128
|
+
while ((at = rest.indexOf("\0")) >= 0) {
|
|
129
|
+
const line = rest.slice(0, at);
|
|
130
|
+
rest = rest.slice(at + 1);
|
|
131
|
+
const tab = line.indexOf("\t");
|
|
132
|
+
const bytes = Number(line.slice(0, tab));
|
|
133
|
+
if (tab < 0 || !Number.isFinite(bytes)) continue;
|
|
134
|
+
const name = line.slice(tab + 1).replace(/^\.[\\/]/, "");
|
|
135
|
+
answer(name, bytes, quotedNames(errors).has(name));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
Promise.all([stdoutDone, errorsDone, child.exited]).then(() => {
|
|
140
|
+
// A message that reached stderr after its folder's line still makes that folder a floor.
|
|
141
|
+
const short = quotedNames(errors);
|
|
142
|
+
for (const [name, bytes] of answered) if (short.has(name) && !known.get(resolve(parent, name))?.partial) answer(name, bytes, true);
|
|
143
|
+
job = null;
|
|
144
|
+
if (mine.cancelled) return send({ t: "dirsizedone", cancelled: true });
|
|
145
|
+
run();
|
|
146
|
+
});
|
|
147
|
+
}
|