flea-qml 0.0.0-stage → 0.1.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 +478 -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 +402 -0
- package/src/archiveops.js +697 -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 +129 -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/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +326 -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 +241 -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-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-html-security.mjs +23 -0
- package/test/manual-html-ui.mjs +30 -0
- package/test/manual-syntax-preview.mjs +26 -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 +68 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +771 -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 +523 -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 +115 -0
- package/ui/Pane.qml +1296 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +239 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +605 -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 +1004 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +66 -0
- package/ui/PreviewColumn.qml +551 -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 +38 -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 +732 -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/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 +827 -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 +122 -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 +364 -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 +502 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +305 -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 +736 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +174 -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 +81 -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 +228 -0
package/docs/temml.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Temml source and inherited notices
|
|
2
|
+
|
|
3
|
+
`src/vendor/temml.mjs` is the unmodified Temml 0.14.0 npm distribution:
|
|
4
|
+
|
|
5
|
+
- Source: https://cdn.jsdelivr.net/npm/temml@0.14.0/dist/temml.mjs
|
|
6
|
+
- Size: 478952 bytes
|
|
7
|
+
- SHA-256: `09e55a0c364d35b8ef66be49486b90884240a7e66c463a825e096faf27b0d59d`
|
|
8
|
+
- Licence: [MIT-temml.txt](../src/vendor/LICENSES/MIT-temml.txt).
|
|
9
|
+
|
|
10
|
+
No code or embedded attribution comments were changed. These additional notices
|
|
11
|
+
identify inherited work; historical source references do not imply that whole
|
|
12
|
+
source files are byte-identical to Temml's adapted implementation.
|
|
13
|
+
|
|
14
|
+
## KaTeX and Perseus
|
|
15
|
+
|
|
16
|
+
Temml's [0.14.0 README](https://cdn.jsdelivr.net/npm/temml@0.14.0/README.md)
|
|
17
|
+
identifies it as a KaTeX fork and credits Khan Academy and KaTeX contributors.
|
|
18
|
+
[MIT-katex.txt](../src/vendor/LICENSES/MIT-katex.txt) is copied unchanged from
|
|
19
|
+
[KaTeX v0.16.0](https://github.com/KaTeX/KaTeX/blob/v0.16.0/LICENSE).
|
|
20
|
+
This attribution reference is not a claim that Temml forked exactly that tag.
|
|
21
|
+
|
|
22
|
+
Temml's `findEndOfMath` retains a comment crediting Khan/Perseus. The same
|
|
23
|
+
brace-scanning algorithm and attribution appear in
|
|
24
|
+
[KaTeX's 2015 auto-render source](https://github.com/KaTeX/KaTeX/blob/cd9bca4a89eddd77c542e23a4a9dc2deff2ab72c/contrib/auto-render/splitAtDelimiters.js).
|
|
25
|
+
Its [contemporaneous licence](https://github.com/KaTeX/KaTeX/blob/cd9bca4a89eddd77c542e23a4a9dc2deff2ab72c/LICENSE.txt)
|
|
26
|
+
is retained unchanged as [MIT-katex-2015.txt](../src/vendor/LICENSES/MIT-katex-2015.txt),
|
|
27
|
+
including its original Underscore credit. This does not mean the full
|
|
28
|
+
Underscore library is shipped.
|
|
29
|
+
|
|
30
|
+
[MIT-perseus.txt](../src/vendor/LICENSES/MIT-perseus.txt) preserves the official
|
|
31
|
+
[Perseus licence](https://raw.githubusercontent.com/Khan/perseus/master/LICENSE)
|
|
32
|
+
retrieved on 2026-11-16. The precise original Perseus revision is not recorded
|
|
33
|
+
in Temml's comment. This modern licence is supplementary attribution, not proof
|
|
34
|
+
of the original revision's terms; the historical KaTeX source and licence above
|
|
35
|
+
document the traceable adaptation.
|
|
36
|
+
|
|
37
|
+
## React utilities
|
|
38
|
+
|
|
39
|
+
Temml explicitly credits Facebook's React under Apache-2.0 for `hyphenate` and
|
|
40
|
+
`escape`. Historical references exist in React v0.3.0:
|
|
41
|
+
[hyphenate](https://github.com/facebook/react/blob/v0.3.0/src/vendor/core/hyphenate.js)
|
|
42
|
+
and [escapeTextForBrowser](https://github.com/facebook/react/blob/v0.3.0/src/utils/escapeTextForBrowser.js).
|
|
43
|
+
Its [Apache-2.0 licence](https://github.com/facebook/react/blob/v0.3.0/LICENSE)
|
|
44
|
+
matches our [Apache-2.0.txt](../src/vendor/LICENSES/Apache-2.0.txt), apart from
|
|
45
|
+
the latter's leading blank line.
|
|
46
|
+
|
|
47
|
+
These are adapted utilities: Temml's escape helper converts to a string and
|
|
48
|
+
uses a smaller replacement table. The exact React commit originally used is
|
|
49
|
+
not identified by Temml; v0.3.0 documents the Apache-licensed historical source,
|
|
50
|
+
not an assertion that it was the precise imported revision. Preserve the
|
|
51
|
+
embedded attribution rather than substituting a later React release's licence.
|
|
52
|
+
|
|
53
|
+
## Chemistry and physics extensions
|
|
54
|
+
|
|
55
|
+
The shipped file preserves the original notices and adaptation descriptions for:
|
|
56
|
+
|
|
57
|
+
- mhchem: Copyright (c) 2011–2015 The MathJax Consortium;
|
|
58
|
+
Copyright (c) 2015–2018 Martin Hensel.
|
|
59
|
+
- Physics macros: Copyright (c) 2015–2016 Kolen Cheung.
|
|
60
|
+
|
|
61
|
+
Their Apache-2.0 notices remain embedded in `temml.mjs`; the full licence is in
|
|
62
|
+
[Apache-2.0.txt](../src/vendor/LICENSES/Apache-2.0.txt). Temml's revision notices
|
|
63
|
+
are also retained. Temml's MIT licence does not replace these inherited credits.
|
package/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {fileURLToPath, pathToFileURL} from 'node:url';
|
|
3
|
+
import {resolve, dirname} from 'node:path';
|
|
4
|
+
import info from './package.json' with {type:'json'};
|
|
5
|
+
|
|
6
|
+
export const entryURL = pathToFileURL(resolve(dirname(fileURLToPath(import.meta.url)), 'ui/boot/shell.qml')).href;
|
|
7
|
+
const hasFrontend = args => args[0]?.startsWith('--') && args[0] !== '--';
|
|
8
|
+
|
|
9
|
+
// Read the package specification only; never execute the script through a shell.
|
|
10
|
+
export function browserPackage(script = info.scripts.browser) {
|
|
11
|
+
const match = /^bunx\s+(buninu-browser@[^\s;&|<>`$]+)\s*$/.exec(script ?? '');
|
|
12
|
+
if (!match) throw new Error('scripts.browser must be "bunx buninu-browser@VERSION"');
|
|
13
|
+
return match[1];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Match buninu-browser's Casty launcher: npx needs Bun for the bin shebang.
|
|
17
|
+
export function browserCommand(args, which = Bun.which, runtime = process.argv0) {
|
|
18
|
+
const pkg = browserPackage();
|
|
19
|
+
const npx = which('npx'), bun = which('bun');
|
|
20
|
+
if (npx && bun) return [npx, '--loglevel=error', '-p', pkg, '--', 'buninu-browser', ...args];
|
|
21
|
+
const bunx = which('bunx');
|
|
22
|
+
return bunx ? [bunx, pkg, ...args] : [bun ?? runtime, 'x', pkg, ...args];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function selectFrontend(args, kitty, platform = process.platform) {
|
|
26
|
+
if (hasFrontend(args)) {
|
|
27
|
+
const [first, ...rest] = args;
|
|
28
|
+
return {frontend:first, args:rest};
|
|
29
|
+
}
|
|
30
|
+
return {frontend:kitty ? '--casty' : platform === 'win32' ? '--win32' : '--wayland', args};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// An actual inline graphics query, not a TERM-name guess. Restore stdin before
|
|
34
|
+
// launching the child, and preserve input unrelated to the query response.
|
|
35
|
+
export async function detectKitty(input = process.stdin, output = process.stdout, timeout = 800) {
|
|
36
|
+
if (!input.isTTY || !output.isTTY || typeof input.setRawMode !== 'function') return false;
|
|
37
|
+
const id = 100000 + Math.floor(Math.random()*1000000000);
|
|
38
|
+
const marker = Buffer.from(`\x1b_Gi=${id};`), end = Buffer.from('\x1b\\');
|
|
39
|
+
const wasRaw = input.isRaw, wasPaused = input.isPaused();
|
|
40
|
+
let bytes = Buffer.alloc(0), timer, onData, onError, onSignal;
|
|
41
|
+
try {
|
|
42
|
+
input.setRawMode(true);
|
|
43
|
+
return await new Promise((resolve, reject) => {
|
|
44
|
+
const finish = value => {clearTimeout(timer); resolve(value);};
|
|
45
|
+
onError = error => {clearTimeout(timer); reject(error);};
|
|
46
|
+
onSignal = () => onError(new Error('Interrupted during terminal detection'));
|
|
47
|
+
onData = chunk => {
|
|
48
|
+
bytes = Buffer.concat([bytes, Buffer.from(chunk)]);
|
|
49
|
+
const start = bytes.indexOf(marker);
|
|
50
|
+
const stop = start < 0 ? -1 : bytes.indexOf(end, start + marker.length);
|
|
51
|
+
if (stop < 0) {if (bytes.length > 65536) finish(false);return;}
|
|
52
|
+
const reply = bytes.subarray(start + marker.length, stop).toString();
|
|
53
|
+
bytes = Buffer.concat([bytes.subarray(0,start), bytes.subarray(stop+end.length)]);
|
|
54
|
+
finish(reply === 'OK' || reply.startsWith('OK '));
|
|
55
|
+
};
|
|
56
|
+
input.on('data', onData); input.on('error', onError); output.on('error', onError);
|
|
57
|
+
for (const signal of ['SIGINT','SIGTERM','SIGHUP']) process.on(signal,onSignal);
|
|
58
|
+
timer = setTimeout(() => finish(false), timeout);
|
|
59
|
+
input.resume();
|
|
60
|
+
output.write(`\x1b_Ga=q,t=d,f=24,s=1,v=1,i=${id},q=0;AAAA\x1b\\`);
|
|
61
|
+
});
|
|
62
|
+
} finally {
|
|
63
|
+
clearTimeout(timer);input.pause();
|
|
64
|
+
if (onData) input.off('data',onData);
|
|
65
|
+
if (onError) {input.off('error',onError);output.off('error',onError);}
|
|
66
|
+
if (onSignal) for (const signal of ['SIGINT','SIGTERM','SIGHUP']) process.off(signal,onSignal);
|
|
67
|
+
input.setRawMode(!!wasRaw);
|
|
68
|
+
if (bytes.length) input.unshift(bytes);
|
|
69
|
+
if (!wasPaused) input.resume();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function main(args = Bun.argv.slice(2)) {
|
|
74
|
+
if (args[0] === '--readme') {
|
|
75
|
+
process.stdout.write(Bun.markdown.ansi(await Bun.file(new URL('./README.md', import.meta.url)).text(), {hyperlinks:true}));
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
if (['-h','--help'].includes(args[0])) {
|
|
79
|
+
console.log(`Usage: flea-qml [BROWSER_FRONTEND] [BROWSER_ARGS...]\n\nAutomatically probes Kitty graphics; otherwise Windows uses --win32,\nother platforms (including Linux/Android) use --wayland. The first argument\nis passed unchanged to buninu-browser and overrides detection (for example
|
|
80
|
+
--casty, --win32, --wayland or --headless). Requires Bun on PATH. Remaining arguments go unchanged\nto buninu-browser after the absolute file URL of ui/boot/shell.qml.\n\n --readme Print the README as coloured ANSI Markdown\n --version, -V Print the package version\n --help, -h Show this help`);
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
if (['-V','--version'].includes(args[0])) {console.log(`flea-qml ${info.version}\n${browserPackage()} (pinned)`);return 0;}
|
|
84
|
+
let kitty = false;
|
|
85
|
+
if (!hasFrontend(args)) {
|
|
86
|
+
try {kitty = await detectKitty();}
|
|
87
|
+
finally {
|
|
88
|
+
// The child inherits the terminal. The probe must neither consume its
|
|
89
|
+
// input nor keep this launcher alive after the child has exited.
|
|
90
|
+
process.stdin.pause();
|
|
91
|
+
process.stdin.unref?.();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const selection = selectFrontend(args, kitty);
|
|
95
|
+
const browserArgs = [selection.frontend, entryURL, ...selection.args];
|
|
96
|
+
const child = Bun.spawn({cmd:browserCommand(browserArgs),stdio:['inherit','inherit','inherit']});
|
|
97
|
+
const handlers = ['SIGINT','SIGTERM','SIGHUP'].map(signal => {
|
|
98
|
+
const handler = () => {try {child.kill(signal);} catch {}};
|
|
99
|
+
process.on(signal,handler);return [signal,handler];
|
|
100
|
+
});
|
|
101
|
+
try {return (await child.exited) ?? 1;}
|
|
102
|
+
finally {for(const [signal,handler] of handlers) process.off(signal,handler);}
|
|
103
|
+
}
|
|
104
|
+
if (import.meta.main) {
|
|
105
|
+
try {process.exitCode = await main();}
|
|
106
|
+
catch (error) {console.error('flea-qml: '+error.message);process.exitCode=1;}
|
|
107
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flea-qml",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Flea QML is a cross-platform file manager written in Bun JavaScript & QML. It's an unofficial fork of Flea, the fast GUI file manager for Omarchy",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"flea-qml": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"private": false,
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Dr. John (醫者小智)",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/jjtseng93/flea-qml.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/jjtseng93/flea-qml",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/jjtseng93/flea-qml/issues"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"bun",
|
|
26
|
+
"qml",
|
|
27
|
+
"file-manager",
|
|
28
|
+
"quickshell",
|
|
29
|
+
"buninu"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"start": "bun index.js",
|
|
33
|
+
"test": "bun test",
|
|
34
|
+
"browser": "bunx buninu-browser@0.10.4"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/archive.js
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
// Archive listings for the preview: what `tar -tf` and `unzip -l` would print, as
|
|
2
|
+
// { name, dir, size } per member. The header and central-directory parsers come from
|
|
3
|
+
// buninu-linux's initramfs/bin/tar and initramfs/bin/unzip@da0b394 (MIT, Dr. John), changed to read
|
|
4
|
+
// through a descriptor: a zip reads its tail and central directory only, and a plain tar reads
|
|
5
|
+
// each header and seeks past the member's data, so neither loads the archive. A compressed tar
|
|
6
|
+
// cannot be sought through, so it is inflated as a stream and its member data dropped as it passes.
|
|
7
|
+
import { closeSync, constants, fstatSync, openSync, readSync } from "node:fs";
|
|
8
|
+
import { Readable } from "node:stream";
|
|
9
|
+
import { createGunzip, createZstdDecompress, inflateRawSync } from "node:zlib";
|
|
10
|
+
|
|
11
|
+
const BLOCK = 512;
|
|
12
|
+
const MAX_RECORD = 1024 * 1024;
|
|
13
|
+
function checkedSize(value) {
|
|
14
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new Error("invalid archive size");
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
const decoder = new TextDecoder(),
|
|
18
|
+
strictUtf8 = new TextDecoder("utf-8", { fatal: true });
|
|
19
|
+
|
|
20
|
+
// Reads at an offset through a descriptor, or from bytes already in memory; short at the end.
|
|
21
|
+
function fileReader(fd, size) {
|
|
22
|
+
return {
|
|
23
|
+
size,
|
|
24
|
+
read(position, length) {
|
|
25
|
+
checkedSize(position);
|
|
26
|
+
checkedSize(length);
|
|
27
|
+
if (position > size || length > size - position) throw new Error("truncated archive");
|
|
28
|
+
const n = length,
|
|
29
|
+
buffer = Buffer.alloc(n);
|
|
30
|
+
let done = 0;
|
|
31
|
+
while (done < n) {
|
|
32
|
+
const count = readSync(fd, buffer, done, n - done, position + done);
|
|
33
|
+
if (count === 0) break;
|
|
34
|
+
done += count;
|
|
35
|
+
}
|
|
36
|
+
return done === n ? buffer : buffer.subarray(0, done);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ---- tar (buninu-linux initramfs/bin/tar scanEntries) ----
|
|
42
|
+
|
|
43
|
+
const text = (block, offset, length) => {
|
|
44
|
+
let end = offset;
|
|
45
|
+
const stop = offset + length;
|
|
46
|
+
while (end < stop && block[end] !== 0) end++;
|
|
47
|
+
return decoder.decode(block.subarray(offset, end));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const octal = (block, offset, length) => {
|
|
51
|
+
// GNU base-256 encoding for values that do not fit the octal field.
|
|
52
|
+
if (block[offset] & 0x80) {
|
|
53
|
+
let value = block[offset] & 0x7f;
|
|
54
|
+
for (let index = offset + 1; index < offset + length; index++) value = value * 256 + block[index];
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
const value = Number.parseInt(text(block, offset, length).trim(), 8);
|
|
58
|
+
return Number.isFinite(value) ? value : 0;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// The header checksum is computed with its own field read as spaces. A mismatch means this is
|
|
62
|
+
// not a tar header, so the listing fails rather than showing partial totals.
|
|
63
|
+
const checksumMatches = (block) => {
|
|
64
|
+
let sum = 0;
|
|
65
|
+
for (let index = 0; index < BLOCK; index++) sum += index >= 148 && index < 156 ? 0x20 : block[index];
|
|
66
|
+
return sum === octal(block, 148, 8);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Each record's length counts bytes, so the records are cut from the bytes and decoded one by one;
|
|
70
|
+
// cutting a decoded string by that length breaks on any multi-byte name.
|
|
71
|
+
const paxRecords = (data) => {
|
|
72
|
+
const records = Object.create(null);
|
|
73
|
+
let offset = 0;
|
|
74
|
+
while (offset < data.length) {
|
|
75
|
+
const space = data.indexOf(0x20, offset);
|
|
76
|
+
if (space === -1) break;
|
|
77
|
+
const length = Number.parseInt(decoder.decode(data.subarray(offset, space)), 10);
|
|
78
|
+
if (!Number.isFinite(length) || length <= 0 || offset + length > data.length) break;
|
|
79
|
+
const record = decoder.decode(data.subarray(space + 1, offset + length)).replace(/\n$/, "");
|
|
80
|
+
const equals = record.indexOf("=");
|
|
81
|
+
if (equals > 0) records[record.slice(0, equals)] = record.slice(equals + 1);
|
|
82
|
+
offset += length;
|
|
83
|
+
}
|
|
84
|
+
return records;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// The header logic both readers share: POSIX ustar, GNU long names (L/K), pax headers (x/X/g), and
|
|
88
|
+
// tar streams joined end to end, as APK v2 does. `header` answers how many bytes of the member that
|
|
89
|
+
// follows are wanted (a long name or pax record) and how many to skip; `record` takes the wanted ones.
|
|
90
|
+
class TarHeaders {
|
|
91
|
+
constructor(visit) {
|
|
92
|
+
this.visit = visit;
|
|
93
|
+
this.longName = null;
|
|
94
|
+
this.extended = {};
|
|
95
|
+
this.global = {};
|
|
96
|
+
this.seen = 0;
|
|
97
|
+
this.done = false;
|
|
98
|
+
this.pending = null;
|
|
99
|
+
}
|
|
100
|
+
// Reject invalid headers even after valid members.
|
|
101
|
+
header(block) {
|
|
102
|
+
if (block.every((byte) => byte === 0)) {
|
|
103
|
+
this.longName = null;
|
|
104
|
+
this.extended = {};
|
|
105
|
+
this.global = {};
|
|
106
|
+
return { want: 0, skip: 0 };
|
|
107
|
+
}
|
|
108
|
+
if (!checksumMatches(block)) {
|
|
109
|
+
throw new Error("invalid tar header");
|
|
110
|
+
}
|
|
111
|
+
this.seen++;
|
|
112
|
+
const flag = String.fromCharCode(block[156] || 0x30);
|
|
113
|
+
const metadata = ["L", "K", "x", "X", "g"].includes(flag);
|
|
114
|
+
const attrs = { ...this.global, ...this.extended };
|
|
115
|
+
const size = checkedSize(!metadata && attrs.size !== undefined ? Number(attrs.size) : octal(block, 124, 12));
|
|
116
|
+
const padded = checkedSize(Math.ceil(size / BLOCK) * BLOCK);
|
|
117
|
+
if (metadata) {
|
|
118
|
+
if (size > MAX_RECORD) throw new Error("tar metadata record is too large");
|
|
119
|
+
this.pending = flag;
|
|
120
|
+
return { want: size, skip: padded - size };
|
|
121
|
+
}
|
|
122
|
+
if (flag !== "g") {
|
|
123
|
+
const prefix = text(block, 257, 5) === "ustar" ? text(block, 345, 155) : "",
|
|
124
|
+
stored = text(block, 0, 100);
|
|
125
|
+
const name = (attrs.path ?? this.longName ?? (prefix ? `${prefix}/${stored}` : stored)).replace(/^\.\//, "");
|
|
126
|
+
if (name) this.visit({ name: name.replace(/\/$/, ""), dir: flag === "5" || name.endsWith("/"), size: flag === "5" ? 0 : size });
|
|
127
|
+
this.longName = null;
|
|
128
|
+
this.extended = {};
|
|
129
|
+
}
|
|
130
|
+
return { want: 0, skip: padded };
|
|
131
|
+
}
|
|
132
|
+
record(data) {
|
|
133
|
+
if (this.pending === "L") this.longName = decoder.decode(data).replace(/\0+$/, "");
|
|
134
|
+
else if (this.pending === "x" || this.pending === "X") this.extended = { ...this.extended, ...paxRecords(data) };
|
|
135
|
+
else if (this.pending === "g") this.global = { ...this.global, ...paxRecords(data) };
|
|
136
|
+
this.pending = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// A plain tar through its descriptor: one read per header, a seek past every member's data.
|
|
141
|
+
function scanTar(reader, visit) {
|
|
142
|
+
const headers = new TarHeaders(visit);
|
|
143
|
+
for (let offset = 0; offset < reader.size; ) {
|
|
144
|
+
const block = reader.read(offset, BLOCK);
|
|
145
|
+
if (block.length < BLOCK) break;
|
|
146
|
+
offset += BLOCK;
|
|
147
|
+
const { want, skip } = headers.header(block);
|
|
148
|
+
if (want + skip > reader.size - offset) throw new Error("truncated tar member");
|
|
149
|
+
if (want || headers.pending) headers.record(reader.read(offset, want));
|
|
150
|
+
offset += want + skip;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// A compressed tar, inflated as a stream: headers and their small records are kept, member data is
|
|
155
|
+
// counted past and dropped, so memory stays one chunk however large the archive unpacks.
|
|
156
|
+
function streamTar(fd, size, inflate, visit) {
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
const headers = new TarHeaders(visit),
|
|
159
|
+
// Keep ownership of the checked descriptor here. Some Bun versions close an
|
|
160
|
+
// fs.ReadStream's fd on destroy even when autoClose is false.
|
|
161
|
+
input = Readable.from((function* () {
|
|
162
|
+
const reader = fileReader(fd, size);
|
|
163
|
+
for (let offset = 0; offset < size; offset += 65536)
|
|
164
|
+
yield reader.read(offset, Math.min(65536, size - offset));
|
|
165
|
+
})()),
|
|
166
|
+
output = input.pipe(inflate);
|
|
167
|
+
let block = Buffer.alloc(0),
|
|
168
|
+
want = 0,
|
|
169
|
+
wanted = [],
|
|
170
|
+
skip = 0;
|
|
171
|
+
let settled = false;
|
|
172
|
+
const finish = (error) => {
|
|
173
|
+
if (settled) return;
|
|
174
|
+
settled = true;
|
|
175
|
+
input.destroy();
|
|
176
|
+
output.destroy();
|
|
177
|
+
error ? reject(error) : resolve();
|
|
178
|
+
};
|
|
179
|
+
output.on("data", (chunk) => {
|
|
180
|
+
try {
|
|
181
|
+
while (chunk.length && !headers.done) {
|
|
182
|
+
// A record's bytes come first, then the padding to its block, so want before skip.
|
|
183
|
+
if (want) {
|
|
184
|
+
const n = Math.min(want, chunk.length);
|
|
185
|
+
wanted.push(chunk.subarray(0, n));
|
|
186
|
+
want -= n;
|
|
187
|
+
chunk = chunk.subarray(n);
|
|
188
|
+
if (!want) {
|
|
189
|
+
headers.record(Buffer.concat(wanted));
|
|
190
|
+
wanted = [];
|
|
191
|
+
}
|
|
192
|
+
} else if (skip) {
|
|
193
|
+
const n = Math.min(skip, chunk.length);
|
|
194
|
+
skip -= n;
|
|
195
|
+
chunk = chunk.subarray(n);
|
|
196
|
+
} else {
|
|
197
|
+
const n = Math.min(BLOCK - block.length, chunk.length);
|
|
198
|
+
block = Buffer.concat([block, chunk.subarray(0, n)]);
|
|
199
|
+
chunk = chunk.subarray(n);
|
|
200
|
+
if (block.length === BLOCK) {
|
|
201
|
+
({ want, skip } = headers.header(block));
|
|
202
|
+
if (!want && headers.pending) headers.record(Buffer.alloc(0));
|
|
203
|
+
block = Buffer.alloc(0);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (headers.done) finish();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
finish(error);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
output.on("end", () => finish(want || skip || block.length ? new Error("truncated tar stream") : undefined));
|
|
213
|
+
output.on("error", finish);
|
|
214
|
+
input.on("error", finish);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ---- zip (buninu-linux initramfs/bin/unzip) ----
|
|
219
|
+
|
|
220
|
+
function scanZip(reader, visit) {
|
|
221
|
+
// End of central directory: in the last 64 KiB + 22 bytes, searched backwards.
|
|
222
|
+
const tailLength = Math.min(reader.size, 65557),
|
|
223
|
+
tail = reader.read(reader.size - tailLength, tailLength);
|
|
224
|
+
let eocd = -1;
|
|
225
|
+
for (let offset = tailLength - 22; offset >= 0; offset--)
|
|
226
|
+
if (tail.readUInt32LE(offset) === 0x06054b50 && offset + 22 + tail.readUInt16LE(offset + 20) === tail.length) {
|
|
227
|
+
eocd = offset;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
if (eocd < 0) throw new Error("not a zip archive");
|
|
231
|
+
// The bounds below follow buninu-linux's initramfs/bin/unzip: one disk only, and every range
|
|
232
|
+
// inside the space before the record that names it.
|
|
233
|
+
if (tail.readUInt16LE(eocd + 4) !== 0 || tail.readUInt16LE(eocd + 6) !== 0 || tail.readUInt16LE(eocd + 8) !== tail.readUInt16LE(eocd + 10))
|
|
234
|
+
throw new Error("split archives are not supported");
|
|
235
|
+
const uint64 = (buffer, offset) => checkedSize(Number(buffer.readBigUInt64LE(offset)));
|
|
236
|
+
let entryCount = tail.readUInt16LE(eocd + 10),
|
|
237
|
+
directorySize = tail.readUInt32LE(eocd + 12),
|
|
238
|
+
directoryOffset = tail.readUInt32LE(eocd + 16),
|
|
239
|
+
directoryLimit = reader.size - tailLength + eocd;
|
|
240
|
+
// zip64: the locator sits just before the end of central directory record.
|
|
241
|
+
if (eocd >= 20 && tail.readUInt32LE(eocd - 20) === 0x07064b50) {
|
|
242
|
+
if (tail.readUInt32LE(eocd - 16) !== 0 || tail.readUInt32LE(eocd - 4) !== 1) throw new Error("split zip64 archives are not supported");
|
|
243
|
+
const recordOffset = uint64(tail, eocd - 20 + 8);
|
|
244
|
+
if (recordOffset > directoryLimit - 20 - 56) throw new Error("bad zip64 record range");
|
|
245
|
+
const record = reader.read(recordOffset, 56);
|
|
246
|
+
if (record.length < 56 || record.readUInt32LE(0) !== 0x06064b50) throw new Error("bad zip64 end of central directory");
|
|
247
|
+
if (record.readUInt32LE(16) !== 0 || record.readUInt32LE(20) !== 0 || uint64(record, 24) !== uint64(record, 32) || uint64(record, 4) < 44)
|
|
248
|
+
throw new Error("bad or split zip64 directory");
|
|
249
|
+
directoryLimit = recordOffset;
|
|
250
|
+
entryCount = uint64(record, 32);
|
|
251
|
+
directorySize = uint64(record, 40);
|
|
252
|
+
directoryOffset = uint64(record, 48);
|
|
253
|
+
}
|
|
254
|
+
checkedSize(entryCount);
|
|
255
|
+
checkedSize(directoryOffset);
|
|
256
|
+
checkedSize(directorySize);
|
|
257
|
+
if (directoryOffset > directoryLimit || directorySize > directoryLimit - directoryOffset || entryCount > Math.floor(directorySize / 46))
|
|
258
|
+
throw new Error("bad central directory bounds");
|
|
259
|
+
let positionInDirectory = 0;
|
|
260
|
+
for (let index = 0; index < entryCount; index++) {
|
|
261
|
+
if (directorySize - positionInDirectory < 46) throw new Error("truncated central directory");
|
|
262
|
+
const header = reader.read(directoryOffset + positionInDirectory, 46);
|
|
263
|
+
const length = 46 + header.readUInt16LE(28) + header.readUInt16LE(30) + header.readUInt16LE(32);
|
|
264
|
+
if (length > directorySize - positionInDirectory) throw new Error("truncated central entry");
|
|
265
|
+
const central = reader.read(directoryOffset + positionInDirectory, length), offset = 0;
|
|
266
|
+
if (central.readUInt32LE(0) !== 0x02014b50) throw new Error("bad central directory");
|
|
267
|
+
if (central.readUInt16LE(34) !== 0) throw new Error("split members are not supported");
|
|
268
|
+
const flags = central.readUInt16LE(offset + 8),
|
|
269
|
+
nameLength = central.readUInt16LE(offset + 28),
|
|
270
|
+
extraLength = central.readUInt16LE(offset + 30),
|
|
271
|
+
commentLength = central.readUInt16LE(offset + 32);
|
|
272
|
+
let size = central.readUInt32LE(offset + 24),
|
|
273
|
+
compressed = central.readUInt32LE(offset + 20),
|
|
274
|
+
local = central.readUInt32LE(offset + 42),
|
|
275
|
+
mtime;
|
|
276
|
+
const wide = [size, compressed, local].filter((value) => value === 0xffffffff).length;
|
|
277
|
+
let foundWide = false;
|
|
278
|
+
// Info-ZIP writes UTF-8 names without the 0x800 flag, so a name that decodes as UTF-8 is read
|
|
279
|
+
// as UTF-8 and only the rest as the old code page's stand-in, latin1.
|
|
280
|
+
const raw = central.subarray(offset + 46, offset + 46 + nameLength);
|
|
281
|
+
let name;
|
|
282
|
+
try {
|
|
283
|
+
name = flags & 0x800 ? raw.toString("utf8") : strictUtf8.decode(raw);
|
|
284
|
+
} catch {
|
|
285
|
+
name = raw.toString("latin1");
|
|
286
|
+
}
|
|
287
|
+
// zip64 extra field: the 0xFFFFFFFF fields follow, uncompressed size first.
|
|
288
|
+
const extra = central.subarray(offset + 46 + nameLength, offset + 46 + nameLength + extraLength);
|
|
289
|
+
for (let position = 0; position + 4 <= extra.length; ) {
|
|
290
|
+
const id = extra.readUInt16LE(position),
|
|
291
|
+
length = extra.readUInt16LE(position + 2);
|
|
292
|
+
if (length > extra.length - position - 4) throw new Error("truncated zip extra field");
|
|
293
|
+
// "UT": the modification time in whole Unix seconds, which the DOS stamp rounds to two.
|
|
294
|
+
if (id === 0x5455 && length >= 5 && extra[position + 4] & 1) mtime = extra.readInt32LE(position + 5);
|
|
295
|
+
if (id === 0x0001 && !foundWide) {
|
|
296
|
+
foundWide = true;
|
|
297
|
+
// Only the fields that overflowed are present, in this order.
|
|
298
|
+
let at = position + 4;
|
|
299
|
+
const end = position + 4 + length;
|
|
300
|
+
const next = () => {
|
|
301
|
+
if (end - at < 8) throw new Error("truncated zip64 size");
|
|
302
|
+
const value = checkedSize(Number(extra.readBigUInt64LE(at)));
|
|
303
|
+
at += 8;
|
|
304
|
+
return value;
|
|
305
|
+
};
|
|
306
|
+
if (size === 0xffffffff) size = next();
|
|
307
|
+
if (compressed === 0xffffffff) compressed = next();
|
|
308
|
+
if (local === 0xffffffff) local = next();
|
|
309
|
+
}
|
|
310
|
+
position += 4 + length;
|
|
311
|
+
}
|
|
312
|
+
if (wide && !foundWide) throw new Error("missing zip64 extra field");
|
|
313
|
+
if (local > directoryOffset || directoryOffset - local < 30 || compressed > directoryOffset - local - 30)
|
|
314
|
+
throw new Error("bad zip member range");
|
|
315
|
+
// The second argument is what reading the member's bytes needs (openZip); a listing ignores it.
|
|
316
|
+
visit({ name: name.replace(/\/$/, ""), dir: name.endsWith("/"), size },
|
|
317
|
+
{ method: central.readUInt16LE(offset + 10), encrypted: (flags & 1) === 1, compressed, local,
|
|
318
|
+
crc: central.readUInt32LE(offset + 16), madeBy: central.readUInt16LE(offset + 4) >> 8,
|
|
319
|
+
external: central.readUInt32LE(offset + 38), time: central.readUInt16LE(offset + 12), date: central.readUInt16LE(offset + 14), mtime });
|
|
320
|
+
positionInDirectory += length;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---- the listing ----
|
|
325
|
+
|
|
326
|
+
const kindOf = (lower) =>
|
|
327
|
+
lower.endsWith(".zip")
|
|
328
|
+
? "zip"
|
|
329
|
+
: lower.endsWith(".tar")
|
|
330
|
+
? "tar"
|
|
331
|
+
: /\.(tar\.gz|tgz)$/.test(lower)
|
|
332
|
+
? "tgz"
|
|
333
|
+
: /\.(tar\.zst|tzst)$/.test(lower)
|
|
334
|
+
? "tzst"
|
|
335
|
+
: "";
|
|
336
|
+
|
|
337
|
+
export const listable = (name) => kindOf(String(name).toLowerCase()) !== "";
|
|
338
|
+
|
|
339
|
+
// Calls visit for every member of the archive at `path`, and settles once the listing is whole;
|
|
340
|
+
// rejects for anything it cannot read. The open is non-blocking and re-checked on the descriptor,
|
|
341
|
+
// so a FIFO is never waited on. A plain tar and a zip are read synchronously; a compressed tar
|
|
342
|
+
// streams.
|
|
343
|
+
export async function listArchive(path, name, visit) {
|
|
344
|
+
const kind = kindOf(String(name).toLowerCase());
|
|
345
|
+
if (!kind) throw new Error("format not read here");
|
|
346
|
+
const fd = openSync(path, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
347
|
+
try {
|
|
348
|
+
const st = fstatSync(fd);
|
|
349
|
+
if (!st.isFile()) throw new Error("not a file");
|
|
350
|
+
const reader = fileReader(fd, st.size);
|
|
351
|
+
if (kind === "zip") return scanZip(reader, visit);
|
|
352
|
+
if (kind === "tar") return scanTar(reader, visit);
|
|
353
|
+
return await streamTar(fd, st.size, kind === "tgz" ? createGunzip() : createZstdDecompress(), visit);
|
|
354
|
+
} finally {
|
|
355
|
+
closeSync(fd);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ---- zip members (src/office.js) ----
|
|
360
|
+
|
|
361
|
+
/** A zip opened for reading single members: `names` in directory order, `sizes` by name, and
|
|
362
|
+
* `read(name, cap)`, which answers the member's bytes, null for a name the zip does not hold, and
|
|
363
|
+
* throws for a member over `cap`, encrypted, or stored by a method other than store and deflate.
|
|
364
|
+
* `close()` releases the descriptor. */
|
|
365
|
+
export function openZip(path) {
|
|
366
|
+
const fd = openSync(path, constants.O_RDONLY | (constants.O_NONBLOCK ?? 0));
|
|
367
|
+
try {
|
|
368
|
+
const st = fstatSync(fd);
|
|
369
|
+
if (!st.isFile()) throw new Error("not a file");
|
|
370
|
+
const reader = fileReader(fd, st.size),
|
|
371
|
+
entries = new Map();
|
|
372
|
+
scanZip(reader, (entry, raw) => {
|
|
373
|
+
if (!entry.dir && !entries.has(entry.name)) entries.set(entry.name, { size: entry.size, ...raw });
|
|
374
|
+
});
|
|
375
|
+
return {
|
|
376
|
+
names: [...entries.keys()],
|
|
377
|
+
sizes: new Map([...entries].map(([name, entry]) => [name, entry.size])),
|
|
378
|
+
read(name, cap) {
|
|
379
|
+
const entry = entries.get(name);
|
|
380
|
+
if (!entry) return null;
|
|
381
|
+
if (entry.encrypted) throw new Error("encrypted zip member");
|
|
382
|
+
if (entry.size > cap) throw new Error("zip member too large");
|
|
383
|
+
const header = reader.read(entry.local, 30);
|
|
384
|
+
if (header.length < 30 || header.readUInt32LE(0) !== 0x04034b50) throw new Error("bad local header");
|
|
385
|
+
const data = reader.read(entry.local + 30 + header.readUInt16LE(26) + header.readUInt16LE(28), entry.compressed);
|
|
386
|
+
if (data.length !== entry.compressed) throw new Error("truncated zip member");
|
|
387
|
+
if (entry.method === 0) return data;
|
|
388
|
+
if (entry.method !== 8) throw new Error("unsupported zip method");
|
|
389
|
+
// The cap bounds the inflated bytes too, whatever size the directory claims.
|
|
390
|
+
return inflateRawSync(data, { maxOutputLength: Math.max(1, cap) });
|
|
391
|
+
},
|
|
392
|
+
close: () => closeSync(fd),
|
|
393
|
+
};
|
|
394
|
+
} catch (error) {
|
|
395
|
+
closeSync(fd);
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// The pieces src/archiveops.js reads member data with: extraction needs the type, mode, link and
|
|
401
|
+
// bytes of every member, which the listing above never keeps.
|
|
402
|
+
export { BLOCK, MAX_RECORD, checkedSize, checksumMatches, fileReader, kindOf, octal, paxRecords, scanZip, text };
|