flea-qml 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +511 -2
- package/THIRD-PARTY-NOTICES.md +29 -0
- package/demos/App.java +5 -0
- package/demos/Dockerfile +4 -0
- package/demos/Main.kt +3 -0
- package/demos/Program.cs +1 -0
- package/demos/README.md +12 -0
- package/demos/app.dart +3 -0
- package/demos/app.deb +0 -0
- package/demos/app.js +1 -0
- package/demos/backup.tar +0 -0
- package/demos/budget.xlsx +0 -0
- package/demos/build.sh +2 -0
- package/demos/bundle.zip +0 -0
- package/demos/clip.mp4 +0 -0
- package/demos/data.sqlite +0 -0
- package/demos/disk.iso +0 -0
- package/demos/engine.cpp +5 -0
- package/demos/firmware.bin +0 -0
- package/demos/flake.nix +4 -0
- package/demos/folder/inside.txt +1 -0
- package/demos/font.ttf +0 -0
- package/demos/index.html +5 -0
- package/demos/index.php +2 -0
- package/demos/init.lua +1 -0
- package/demos/letter.docx +0 -0
- package/demos/lib.rs +3 -0
- package/demos/main.c +6 -0
- package/demos/main.h +3 -0
- package/demos/math-and-diagrams.md +200 -0
- package/demos/notes.tar.gz +0 -0
- package/demos/notes.txt +3 -0
- package/demos/photo.png +0 -0
- package/demos/program +2 -0
- package/demos/report.pdf +32 -0
- package/demos/script.py +1 -0
- package/demos/server.go +7 -0
- package/demos/settings.json +4 -0
- package/demos/slides.pptx +0 -0
- package/demos/song.mp3 +0 -0
- package/demos/style.css +4 -0
- package/demos/tool.rb +1 -0
- package/demos/types.ts +2 -0
- package/docs/pele.md +23 -0
- package/docs/protocol.md +3420 -0
- package/docs/temml.md +63 -0
- package/index.js +107 -0
- package/package.json +34 -4
- package/src/archive.js +407 -0
- package/src/archiveops.js +728 -0
- package/src/backend.js +369 -0
- package/src/bulkrename.js +95 -0
- package/src/clip-host.js +76 -0
- package/src/clip.js +462 -0
- package/src/code.js +168 -0
- package/src/compare.js +74 -0
- package/src/create.js +128 -0
- package/src/dirsize.js +147 -0
- package/src/figure-svg.js +530 -0
- package/src/figure-thread.js +23 -0
- package/src/figure.js +132 -0
- package/src/find.js +55 -0
- package/src/flea.js +39 -0
- package/src/fsinfo.js +39 -0
- package/src/highlight.js +141 -0
- package/src/html.js +52 -0
- package/src/kinds.js +89 -0
- package/src/listing.js +130 -0
- package/src/markdown-html.js +317 -0
- package/src/mathml.js +186 -0
- package/src/menu.js +180 -0
- package/src/meta.js +135 -0
- package/src/office.js +810 -0
- package/src/order.js +100 -0
- package/src/peek.js +49 -0
- package/src/pele-wrapper.js +695 -0
- package/src/permissions.js +180 -0
- package/src/places.js +313 -0
- package/src/preview-language.js +10 -0
- package/src/terminal-server.js +95 -0
- package/src/terminal-shell.js +26 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +327 -0
- package/src/uistate.js +393 -0
- package/src/uistore.js +168 -0
- package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
- package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
- package/src/vendor/LICENSES/MIT-katex.txt +21 -0
- package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
- package/src/vendor/LICENSES/MIT-pele.txt +40 -0
- package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
- package/src/vendor/LICENSES/MIT-temml.txt +21 -0
- package/src/vendor/pele.js +145 -0
- package/src/vendor/temml.mjs +14708 -0
- package/src/watch.js +98 -0
- package/src/wayland/client.js +109 -0
- package/src/wayland/protocol.js +55 -0
- package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
- package/src/wayland/protocols/wayland.xml +3248 -0
- package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
- package/src/wayland/resource-io.js +6 -0
- package/src/wayland/socket-discovery.js +26 -0
- package/src/wayland/transport-posix.js +120 -0
- package/src/wayland/transport-win32.js +11 -0
- package/src/wayland/transport.js +3 -0
- package/src/wayland/win32-resources.js +80 -0
- package/src/wayland/win32-wire.js +62 -0
- package/test/archive.test.js +80 -0
- package/test/bench-markdown-scroll.mjs +190 -0
- package/test/clip-host.test.js +113 -0
- package/test/code-preview.test.js +35 -0
- package/test/compare-ui-paths.test.js +24 -0
- package/test/compare.test.js +54 -0
- package/test/delete-trash.test.js +67 -0
- package/test/dirsize.test.js +138 -0
- package/test/file-ops.test.js +307 -0
- package/test/file-url.test.js +24 -0
- package/test/find-contents.test.js +161 -0
- package/test/find-input.test.js +66 -0
- package/test/fixtures/pele-parity.json +1006 -0
- package/test/highlight-launcher.test.js +35 -0
- package/test/html-preview.test.js +22 -0
- package/test/input.test.js +15 -0
- package/test/keymap.test.js +26 -0
- package/test/language-icons.test.js +36 -0
- package/test/launcher.test.js +51 -0
- package/test/manual-archive-here-ui.mjs +47 -0
- package/test/manual-code-scroll.mjs +28 -0
- package/test/manual-column-scroll.mjs +24 -0
- package/test/manual-compare-ui.mjs +20 -0
- package/test/manual-find-status.mjs +37 -0
- package/test/manual-folder-focus.mjs +63 -0
- package/test/manual-html-security.mjs +23 -0
- package/test/manual-html-ui.mjs +30 -0
- package/test/manual-syntax-preview.mjs +26 -0
- package/test/manual-terminal-lifecycle.mjs +42 -0
- package/test/manual-terminal-navigation.mjs +48 -0
- package/test/manual-terminal-ui.mjs +47 -0
- package/test/manual-trash-ui.mjs +86 -0
- package/test/markdown-diagrams.test.js +93 -0
- package/test/markdown-html.test.js +105 -0
- package/test/mathml.test.js +92 -0
- package/test/menu-transfer.test.js +57 -0
- package/test/office-preview.test.js +154 -0
- package/test/pele-parity.test.js +28 -0
- package/test/places.test.js +134 -0
- package/test/preview-keys.test.js +60 -0
- package/test/preview-language.test.js +22 -0
- package/test/status-filename.test.js +20 -0
- package/test/transfer-replace-merge.test.js +187 -0
- package/test/trash-address.test.js +23 -0
- package/test/trash-view.test.js +37 -0
- package/test/trashbrowse.test.js +135 -0
- package/ui/AboutFacts.qml +78 -0
- package/ui/ArchiveCards.qml +95 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchiveHereConfirm.qml +127 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +775 -0
- package/ui/BulkOldNames.qml +47 -0
- package/ui/BulkRenameDialog.qml +380 -0
- package/ui/CardFrame.qml +67 -0
- package/ui/CardScroll.qml +79 -0
- package/ui/CheckBox.qml +73 -0
- package/ui/ChromeBar.qml +360 -0
- package/ui/ChromeButton.qml +131 -0
- package/ui/ChromeModes.qml +43 -0
- package/ui/CodeLines.qml +256 -0
- package/ui/CodeRefusal.qml +52 -0
- package/ui/CollideConfirm.qml +147 -0
- package/ui/CollideHost.qml +167 -0
- package/ui/ColumnPane.qml +525 -0
- package/ui/ColumnRow.qml +380 -0
- package/ui/ColumnsArea.qml +594 -0
- package/ui/ColumnsLanding.qml +147 -0
- package/ui/CompareView.qml +321 -0
- package/ui/ContextMenu.qml +743 -0
- package/ui/ConvertDialog.qml +456 -0
- package/ui/CountedWorker.qml +7 -0
- package/ui/Crumb.qml +64 -0
- package/ui/CrumbDrop.qml +25 -0
- package/ui/CsvTable.qml +394 -0
- package/ui/CursorMark.qml +41 -0
- package/ui/DefaultClaim.qml +130 -0
- package/ui/DeviceLock.qml +47 -0
- package/ui/DeviceMounts.qml +540 -0
- package/ui/DialogButton.qml +150 -0
- package/ui/DialogField.qml +147 -0
- package/ui/DialogHeader.qml +31 -0
- package/ui/DialogScrim.qml +20 -0
- package/ui/DialogTitle.qml +37 -0
- package/ui/Divider.qml +9 -0
- package/ui/DropInto.qml +126 -0
- package/ui/DropTarget.qml +35 -0
- package/ui/DropboxMark.qml +60 -0
- package/ui/EmptyState.qml +150 -0
- package/ui/EscapeHint.qml +11 -0
- package/ui/FactsTable.qml +54 -0
- package/ui/FastScrollHandler.qml +476 -0
- package/ui/FatNamesConfirm.qml +157 -0
- package/ui/Favourites.qml +143 -0
- package/ui/FigureService.qml +120 -0
- package/ui/FileDrag.qml +267 -0
- package/ui/FilterStrip.qml +133 -0
- package/ui/FitMetrics.qml +22 -0
- package/ui/FlashDriveRows.qml +137 -0
- package/ui/FlashHost.qml +72 -0
- package/ui/FlashImageRow.qml +104 -0
- package/ui/FlashRunner.qml +119 -0
- package/ui/FlashStepRow.qml +172 -0
- package/ui/FlashStrip.qml +38 -0
- package/ui/FlashView.qml +397 -0
- package/ui/FleaMark.qml +88 -0
- package/ui/FlyoutFade.qml +23 -0
- package/ui/FontPane.qml +170 -0
- package/ui/Glass.qml +114 -0
- package/ui/GlassTagger.qml +59 -0
- package/ui/Glyph.qml +64 -0
- package/ui/GlyphSolid.qml +22 -0
- package/ui/GridArea.qml +664 -0
- package/ui/GridTile.qml +325 -0
- package/ui/GvfsBridge.qml +164 -0
- package/ui/Header.qml +433 -0
- package/ui/HoverTimers.qml +39 -0
- package/ui/HtmlClip.qml +30 -0
- package/ui/HtmlPane.qml +63 -0
- package/ui/HyprlandMark.qml +45 -0
- package/ui/Ipc.qml +1005 -0
- package/ui/IpcControls.qml +7 -0
- package/ui/IpcMenuState.qml +48 -0
- package/ui/IpcPreviewState.qml +185 -0
- package/ui/JumpPath.qml +51 -0
- package/ui/KeymapSheet.qml +561 -0
- package/ui/KeymapSheetResult.qml +89 -0
- package/ui/KindStack.qml +40 -0
- package/ui/LangMark.qml +51 -0
- package/ui/LangMarkLoader.qml +26 -0
- package/ui/List.qml +480 -0
- package/ui/LoadingState.qml +54 -0
- package/ui/LocalSend.qml +56 -0
- package/ui/LocalSendMark.qml +73 -0
- package/ui/MarkdownBlockView.qml +311 -0
- package/ui/MarkdownBlocks.qml +35 -0
- package/ui/MarkdownFigure.qml +254 -0
- package/ui/MarkdownImages.qml +72 -0
- package/ui/MarkdownList.qml +81 -0
- package/ui/MarkdownMathsText.qml +97 -0
- package/ui/MarkdownPane.qml +83 -0
- package/ui/MarkdownPictures.qml +41 -0
- package/ui/MarkdownQuote.qml +52 -0
- package/ui/MarkdownRemote.qml +82 -0
- package/ui/MarkdownSource.qml +155 -0
- package/ui/MarkdownTable.qml +222 -0
- package/ui/MarkdownTableScroll.qml +64 -0
- package/ui/MarkdownTableWheel.qml +126 -0
- package/ui/MarkdownText.qml +87 -0
- package/ui/MarkdownWorker.js +132 -0
- package/ui/MatchText.qml +99 -0
- package/ui/MediaSession.qml +38 -0
- package/ui/MediaSound.qml +26 -0
- package/ui/MediaStrip.qml +191 -0
- package/ui/MediaTrack.qml +36 -0
- package/ui/MediaVolume.qml +54 -0
- package/ui/MenuActionDialog.qml +396 -0
- package/ui/MenuEdgeFade.qml +24 -0
- package/ui/MenuHost.qml +227 -0
- package/ui/MenuRow.qml +351 -0
- package/ui/MountListing.qml +100 -0
- package/ui/NetworkDialog.qml +435 -0
- package/ui/NetworkForm.qml +426 -0
- package/ui/NetworkMounts.qml +913 -0
- package/ui/NetworkPlaces.qml +127 -0
- package/ui/OpenWithDialog.qml +613 -0
- package/ui/Opener.qml +95 -0
- package/ui/Pane.qml +1320 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +268 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +601 -0
- package/ui/ParkedWait.qml +9 -0
- package/ui/PathEntry.qml +186 -0
- package/ui/PathJump.qml +400 -0
- package/ui/PdfViewer.qml +297 -0
- package/ui/PermissionsDialog.qml +658 -0
- package/ui/PermissionsHeadings.qml +31 -0
- package/ui/PhoneMounts.qml +67 -0
- package/ui/PickerAnswers.qml +75 -0
- package/ui/PickerBox.qml +26 -0
- package/ui/PickerChrome.qml +295 -0
- package/ui/PickerFilter.qml +97 -0
- package/ui/PickerGrid.qml +308 -0
- package/ui/PickerHeader.qml +28 -0
- package/ui/PickerLifecycle.qml +27 -0
- package/ui/PickerList.qml +218 -0
- package/ui/PickerListing.qml +149 -0
- package/ui/PickerPathBar.qml +127 -0
- package/ui/PickerPlaces.qml +189 -0
- package/ui/PickerRecent.qml +92 -0
- package/ui/PickerSave.qml +101 -0
- package/ui/PickerSelection.qml +155 -0
- package/ui/PickerWindow.qml +846 -0
- package/ui/PowerSectorsReader.qml +10 -0
- package/ui/PrebuildDriver.qml +146 -0
- package/ui/Preview.qml +1005 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +91 -0
- package/ui/PreviewColumn.qml +553 -0
- package/ui/PreviewEdge.qml +63 -0
- package/ui/PreviewFacts.qml +30 -0
- package/ui/PreviewFactsLine.qml +34 -0
- package/ui/PreviewHtml.qml +233 -0
- package/ui/PreviewImage.qml +253 -0
- package/ui/PreviewImagePan.qml +13 -0
- package/ui/PreviewImageSharp.qml +32 -0
- package/ui/PreviewLines.qml +106 -0
- package/ui/PreviewMarkdown.qml +706 -0
- package/ui/PreviewMedia.qml +250 -0
- package/ui/PreviewPdf.qml +482 -0
- package/ui/PreviewScroll.qml +201 -0
- package/ui/PreviewSwap.qml +335 -0
- package/ui/PreviewText.qml +149 -0
- package/ui/ProtocolChip.qml +18 -0
- package/ui/QuickLookArchive.qml +41 -0
- package/ui/QuickLookBar.qml +103 -0
- package/ui/QuickLookPrepare.qml +355 -0
- package/ui/QuickLookSwap.qml +10 -0
- package/ui/QuickLookWarm.qml +65 -0
- package/ui/RailDrop.qml +155 -0
- package/ui/RailHeading.qml +19 -0
- package/ui/RailPlaces.qml +38 -0
- package/ui/RailTrashRow.qml +67 -0
- package/ui/RenameField.qml +353 -0
- package/ui/RendererRetry.qml +11 -0
- package/ui/ResizeHandle.qml +52 -0
- package/ui/Reveal.qml +54 -0
- package/ui/Row.qml +476 -0
- package/ui/RowDrag.qml +213 -0
- package/ui/RowDropTarget.qml +27 -0
- package/ui/RunCard.qml +286 -0
- package/ui/Scripts.qml +65 -0
- package/ui/SearchStrip.qml +232 -0
- package/ui/SelectionBand.qml +140 -0
- package/ui/SelectionPreview.qml +370 -0
- package/ui/SettingsFavourite.qml +122 -0
- package/ui/SettingsGroup.qml +96 -0
- package/ui/SettingsHint.qml +12 -0
- package/ui/SettingsMetrics.qml +29 -0
- package/ui/SettingsPane.qml +178 -0
- package/ui/SettingsPanel.qml +547 -0
- package/ui/SettingsRail.qml +128 -0
- package/ui/SettingsRow.qml +409 -0
- package/ui/SettingsRuler.qml +85 -0
- package/ui/SettingsSegment.qml +85 -0
- package/ui/ShareBrowser.qml +113 -0
- package/ui/ShareLink.qml +67 -0
- package/ui/ShelfPins.qml +101 -0
- package/ui/Sidebar.qml +728 -0
- package/ui/SidebarRow.qml +358 -0
- package/ui/Spinner.qml +60 -0
- package/ui/StableModel.qml +89 -0
- package/ui/StateMessage.qml +84 -0
- package/ui/StatusBar.qml +512 -0
- package/ui/StatusFileName.qml +27 -0
- package/ui/TabBar.qml +673 -0
- package/ui/TabDragGeometry.qml +60 -0
- package/ui/TagMenuMark.qml +52 -0
- package/ui/TagRailSection.qml +53 -0
- package/ui/TagSlot.qml +40 -0
- package/ui/TagState.qml +150 -0
- package/ui/TagSwatch.qml +28 -0
- package/ui/Taildrop.qml +86 -0
- package/ui/TailscaleMark.qml +50 -0
- package/ui/TerminalService.qml +69 -0
- package/ui/TerminalTab.qml +48 -0
- package/ui/TerminalTabs.qml +27 -0
- package/ui/Theme.qml +460 -0
- package/ui/ThemeColors.qml +43 -0
- package/ui/TransferCard.qml +239 -0
- package/ui/TrashConfirm.qml +129 -0
- package/ui/TrashDropHandler.qml +11 -0
- package/ui/TrashHost.qml +202 -0
- package/ui/TrashMonitor.qml +30 -0
- package/ui/TrashToolbar.qml +19 -0
- package/ui/TrashView.qml +492 -0
- package/ui/UnlockDialog.qml +450 -0
- package/ui/UnlockHost.qml +206 -0
- package/ui/UpdateCheck.qml +97 -0
- package/ui/UserDirs.qml +25 -0
- package/ui/ViewState.qml +565 -0
- package/ui/ViewportScrollBar.qml +195 -0
- package/ui/ViewportScrollBars.qml +25 -0
- package/ui/WebPreview.qml +33 -0
- package/ui/WebPreviewView.qml +26 -0
- package/ui/WindowBody.qml +829 -0
- package/ui/WindowDrag.qml +35 -0
- package/ui/boot/Commons/AudioNodes.qml +50 -0
- package/ui/boot/Commons/AudioNodesModel.js +96 -0
- package/ui/boot/Commons/Border.qml +243 -0
- package/ui/boot/Commons/BorderGeometry.js +373 -0
- package/ui/boot/Commons/Color.qml +258 -0
- package/ui/boot/Commons/I18n.qml +118 -0
- package/ui/boot/Commons/InputDevices.qml +112 -0
- package/ui/boot/Commons/InputMethodState.qml +38 -0
- package/ui/boot/Commons/IpcRegistry.qml +77 -0
- package/ui/boot/Commons/Numbers.js +19 -0
- package/ui/boot/Commons/Plural.js +91 -0
- package/ui/boot/Commons/ShellIpc.qml +12 -0
- package/ui/boot/Commons/Style.qml +561 -0
- package/ui/boot/Commons/Translations.qml +100 -0
- package/ui/boot/Commons/UntypedInput.qml +152 -0
- package/ui/boot/Commons/Util.qml +188 -0
- package/ui/boot/Commons/qmldir +13 -0
- package/ui/boot/LICENSE-omarchy +20 -0
- package/ui/boot/Ui/BackgroundMedia.qml +49 -0
- package/ui/boot/Ui/BarIconButton.qml +106 -0
- package/ui/boot/Ui/BarIconContent.qml +38 -0
- package/ui/boot/Ui/BarIndicator.qml +53 -0
- package/ui/boot/Ui/BarWidget.qml +50 -0
- package/ui/boot/Ui/BorderOverlay.qml +54 -0
- package/ui/boot/Ui/BorderSurface.qml +40 -0
- package/ui/boot/Ui/Button.qml +224 -0
- package/ui/boot/Ui/ButtonGroup.qml +131 -0
- package/ui/boot/Ui/ConfirmDialog.qml +134 -0
- package/ui/boot/Ui/CursorSurface.qml +42 -0
- package/ui/boot/Ui/Dropdown.qml +245 -0
- package/ui/boot/Ui/FilterInput.qml +26 -0
- package/ui/boot/Ui/IconContentBounds.qml +141 -0
- package/ui/boot/Ui/KeyboardPanel.qml +453 -0
- package/ui/boot/Ui/MultiSelect.qml +624 -0
- package/ui/boot/Ui/NumberField.qml +86 -0
- package/ui/boot/Ui/OpticalGlyph.qml +68 -0
- package/ui/boot/Ui/OverlayWindow.qml +102 -0
- package/ui/boot/Ui/Panel.qml +60 -0
- package/ui/boot/Ui/PanelActionButton.qml +105 -0
- package/ui/boot/Ui/PanelController.qml +16 -0
- package/ui/boot/Ui/PanelHero.qml +112 -0
- package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
- package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
- package/ui/boot/Ui/PanelSeparator.qml +19 -0
- package/ui/boot/Ui/PanelSlider.qml +156 -0
- package/ui/boot/Ui/PanelToolTip.qml +50 -0
- package/ui/boot/Ui/PluginBarApi.qml +89 -0
- package/ui/boot/Ui/PointerMoveGate.qml +54 -0
- package/ui/boot/Ui/PopupCard.qml +181 -0
- package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
- package/ui/boot/Ui/SearchableDropdown.qml +354 -0
- package/ui/boot/Ui/SizedImage.qml +125 -0
- package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
- package/ui/boot/Ui/TextField.qml +58 -0
- package/ui/boot/Ui/Toggle.qml +118 -0
- package/ui/boot/Ui/ToggleSwitch.qml +115 -0
- package/ui/boot/Ui/WidgetButton.qml +172 -0
- package/ui/boot/Ui/qmldir +41 -0
- package/ui/boot/fleatab.qml +50 -0
- package/ui/boot/picker.qml +39 -0
- package/ui/boot/shell.qml +131 -0
- package/ui/boot/tabtearoff.qml +51 -0
- package/ui/js/Anchor.js +379 -0
- package/ui/js/AnchorHold.js +162 -0
- package/ui/js/Archive.js +157 -0
- package/ui/js/ArchiveCard.js +44 -0
- package/ui/js/ArchiveOps.js +44 -0
- package/ui/js/BackReturn.js +118 -0
- package/ui/js/BulkRename.js +100 -0
- package/ui/js/Buttons.js +54 -0
- package/ui/js/ChromeKeys.js +116 -0
- package/ui/js/ClipMarks.js +73 -0
- package/ui/js/ClipSelection.js +67 -0
- package/ui/js/Clipboard.js +179 -0
- package/ui/js/Cloud.js +97 -0
- package/ui/js/CodeRuns.js +72 -0
- package/ui/js/Collide.js +212 -0
- package/ui/js/ColumnFit.js +28 -0
- package/ui/js/ColumnMenu.js +111 -0
- package/ui/js/Columns.js +298 -0
- package/ui/js/ColumnsCarry.js +103 -0
- package/ui/js/Compare.js +74 -0
- package/ui/js/Contrast.js +157 -0
- package/ui/js/ControlReader.js +42 -0
- package/ui/js/Convert.js +52 -0
- package/ui/js/CopyAs.js +52 -0
- package/ui/js/Crumbs.js +178 -0
- package/ui/js/Csv.js +231 -0
- package/ui/js/Density.js +44 -0
- package/ui/js/Devices.js +440 -0
- package/ui/js/DirSizes.js +180 -0
- package/ui/js/Drag.js +360 -0
- package/ui/js/DragOut.js +166 -0
- package/ui/js/DropDev.js +52 -0
- package/ui/js/Dropbox.js +41 -0
- package/ui/js/Eject.js +217 -0
- package/ui/js/Errors.js +188 -0
- package/ui/js/ExtThumbs.js +115 -0
- package/ui/js/Facts.js +402 -0
- package/ui/js/FigureDate.js +14 -0
- package/ui/js/Filter.js +285 -0
- package/ui/js/Find.js +46 -0
- package/ui/js/Flash.js +184 -0
- package/ui/js/FlashCard.js +79 -0
- package/ui/js/FlashEndings.js +125 -0
- package/ui/js/FlashPane.js +116 -0
- package/ui/js/FleaBin.js +12 -0
- package/ui/js/Focus.js +504 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +314 -0
- package/ui/js/Fuzzy.js +141 -0
- package/ui/js/GlassTag.js +72 -0
- package/ui/js/Grid.js +33 -0
- package/ui/js/GridGeometry.js +90 -0
- package/ui/js/GridNames.js +153 -0
- package/ui/js/GvfsBridge.js +200 -0
- package/ui/js/HostClipboard.js +29 -0
- package/ui/js/Icons.js +218 -0
- package/ui/js/Input.js +50 -0
- package/ui/js/Jump.js +200 -0
- package/ui/js/KeyName.js +64 -0
- package/ui/js/Keymap.js +401 -0
- package/ui/js/Kinds.js +164 -0
- package/ui/js/LICENSES/ISC-lucide.txt +43 -0
- package/ui/js/LangMarkPaths.js +47 -0
- package/ui/js/LangMarks.js +120 -0
- package/ui/js/Lists.js +11 -0
- package/ui/js/LocalSend.js +39 -0
- package/ui/js/LockedMenu.js +63 -0
- package/ui/js/MakeDefault.js +186 -0
- package/ui/js/MarkBox.js +7 -0
- package/ui/js/Markdown.js +119 -0
- package/ui/js/MarkdownLists.js +86 -0
- package/ui/js/MarkdownMaths.js +58 -0
- package/ui/js/MarkdownPictures.js +32 -0
- package/ui/js/MarkdownPrepared.js +139 -0
- package/ui/js/MarkdownTableFit.js +34 -0
- package/ui/js/Marks.js +148 -0
- package/ui/js/Marquee.js +96 -0
- package/ui/js/Match.js +93 -0
- package/ui/js/Material.js +109 -0
- package/ui/js/MdBlocks.js +437 -0
- package/ui/js/MdBreak.js +71 -0
- package/ui/js/MdChunks.js +70 -0
- package/ui/js/MdContainer.js +137 -0
- package/ui/js/MdDocument.js +337 -0
- package/ui/js/MdEmph.js +97 -0
- package/ui/js/MdEntity.js +108 -0
- package/ui/js/MdEntityTable.js +223 -0
- package/ui/js/MdEscape.js +59 -0
- package/ui/js/MdFront.js +37 -0
- package/ui/js/MdHold.js +32 -0
- package/ui/js/MdHtml.js +307 -0
- package/ui/js/MdHtmlBlock.js +290 -0
- package/ui/js/MdHtmlHeading.js +53 -0
- package/ui/js/MdHtmlImage.js +179 -0
- package/ui/js/MdInline.js +154 -0
- package/ui/js/MdItems.js +212 -0
- package/ui/js/MdLeaf.js +309 -0
- package/ui/js/MdLink.js +196 -0
- package/ui/js/MdMath.js +67 -0
- package/ui/js/MdParagraphs.js +138 -0
- package/ui/js/MdRefs.js +232 -0
- package/ui/js/MdResolve.js +226 -0
- package/ui/js/MdRun.js +300 -0
- package/ui/js/MdUrl.js +226 -0
- package/ui/js/Menu.js +514 -0
- package/ui/js/MenuContext.js +85 -0
- package/ui/js/MenuFit.js +13 -0
- package/ui/js/MenuRefresh.js +74 -0
- package/ui/js/MenuWheel.js +35 -0
- package/ui/js/Messages.js +186 -0
- package/ui/js/Motion.js +16 -0
- package/ui/js/Mounts.js +320 -0
- package/ui/js/MouseNav.js +13 -0
- package/ui/js/Nav.js +329 -0
- package/ui/js/NetFs.js +26 -0
- package/ui/js/OpenWith.js +83 -0
- package/ui/js/Ops.js +585 -0
- package/ui/js/Palette.js +33 -0
- package/ui/js/PathBar.js +260 -0
- package/ui/js/Permissions.js +179 -0
- package/ui/js/Phones.js +112 -0
- package/ui/js/Picker.js +353 -0
- package/ui/js/PlaceMenu.js +96 -0
- package/ui/js/Places.js +265 -0
- package/ui/js/PreviewAsk.js +21 -0
- package/ui/js/PreviewKeys.js +155 -0
- package/ui/js/PreviewSettle.js +29 -0
- package/ui/js/PreviewSwap.js +101 -0
- package/ui/js/PropertyFacts.js +144 -0
- package/ui/js/Protocols.js +296 -0
- package/ui/js/QuickLookKinds.js +27 -0
- package/ui/js/QuickLookRect.js +24 -0
- package/ui/js/RailDrop.js +51 -0
- package/ui/js/RailKeys.js +87 -0
- package/ui/js/RailMenu.js +133 -0
- package/ui/js/Recent.js +135 -0
- package/ui/js/RecentMode.js +156 -0
- package/ui/js/Reload.js +60 -0
- package/ui/js/Renderer.js +9 -0
- package/ui/js/RunFile.js +143 -0
- package/ui/js/Scripts.js +28 -0
- package/ui/js/Scroll.js +304 -0
- package/ui/js/ScrollOff.js +69 -0
- package/ui/js/Search.js +478 -0
- package/ui/js/Selection.js +97 -0
- package/ui/js/Settings.js +476 -0
- package/ui/js/SettingsAbout.js +97 -0
- package/ui/js/SettingsShelf.js +52 -0
- package/ui/js/SheetKeys.js +42 -0
- package/ui/js/SheetQuery.js +281 -0
- package/ui/js/ShelfPile.js +27 -0
- package/ui/js/SlowClick.js +53 -0
- package/ui/js/SlowOp.js +6 -0
- package/ui/js/Sort.js +134 -0
- package/ui/js/Startup.js +63 -0
- package/ui/js/StateWords.js +17 -0
- package/ui/js/Status.js +238 -0
- package/ui/js/Swap.js +166 -0
- package/ui/js/TabMove.js +39 -0
- package/ui/js/Tabs.js +751 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +180 -0
- package/ui/js/TextSize.js +107 -0
- package/ui/js/Thumbs.js +158 -0
- package/ui/js/Transfer.js +172 -0
- package/ui/js/Trash.js +84 -0
- package/ui/js/TrashDates.js +47 -0
- package/ui/js/TrashDrop.js +64 -0
- package/ui/js/UiState.js +438 -0
- package/ui/js/Unlock.js +152 -0
- package/ui/js/Update.js +130 -0
- package/ui/js/UserDirs.js +56 -0
- package/ui/qmldir +230 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Menu.js" as Menu
|
|
4
|
+
.import "Mounts.js" as Mounts
|
|
5
|
+
.import "PlaceMenu.js" as PlaceMenu
|
|
6
|
+
|
|
7
|
+
// The rail's own menu: which one a row opens, what a chosen row does, and the Edit that rewrites a
|
|
8
|
+
// saved place. Split out of ui/js/Mounts.js, which parses and identifies rail rows and nothing more.
|
|
9
|
+
|
|
10
|
+
// Which menu a rail row opens, and with which handle: the trash and a favourite carry their own,
|
|
11
|
+
// every other row is a mount or a share and ui/js/Menu.js never sees it. Split out of ui/Sidebar.qml,
|
|
12
|
+
// which sits at its recorded line count, and this file already decides what a row offers.
|
|
13
|
+
function railMenuFor(sidebar, entry, scenePosition, hidden) {
|
|
14
|
+
if (entry.kind === "trash") {
|
|
15
|
+
sidebar.menu.openForRail("trash", Menu.trashEntries(sidebar.trashCount, false), scenePosition)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
// MenuAdditions rule 3: a Places or Favorites row opens the folder menu for its path while that
|
|
19
|
+
// Extras row is on, and with it off a favourite keeps the one Remove it has offered since 0.2.1.
|
|
20
|
+
if (entry.kind === "favourite" || entry.kind === "home") {
|
|
21
|
+
var index = entry.kind === "favourite" ? entry.favouriteIndex : -1
|
|
22
|
+
var rows = PlaceMenu.entries(entry, index, hidden)
|
|
23
|
+
if (rows.length > 0) {
|
|
24
|
+
sidebar.menu.openForRail(PlaceMenu.key(entry, index), rows, scenePosition)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
if (entry.kind !== "favourite")
|
|
28
|
+
return
|
|
29
|
+
// The Places menu is off: the one row is INVENTORY's own Remove from Favorites, never a bare Remove.
|
|
30
|
+
sidebar.menu.openForRail("favourite:" + entry.favouriteIndex + ":" + JSON.stringify(entry.original),
|
|
31
|
+
Menu.railEntries({ group: "favourite", kind: "favourite" }), scenePosition)
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
sidebar.menu.openForRail(Mounts.railKey(entry), Mounts.rowMenu(entry), scenePosition)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Issue 21: Edit opens D11's dialog over the saved place, and the address that finally mounts is
|
|
38
|
+
// written back over that place's own line, so a share gio could not reach is corrected where it sits.
|
|
39
|
+
function editPlace(sidebar, share) {
|
|
40
|
+
var entry = sidebar.networkEntries[share]
|
|
41
|
+
if (!entry)
|
|
42
|
+
return
|
|
43
|
+
sidebar.editingPlace = Mounts.normalize(entry.uri)
|
|
44
|
+
sidebar.editingRequest = ""
|
|
45
|
+
sidebar.editRequested(entry.uri, entry.label, "", "Edit this address, then connect and save.", false, sidebar.navigationPane)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Each attempt this dialog makes, so a mount that was already in flight when the rail armed cannot
|
|
49
|
+
// answer for the edit: a refusal only re-binds, because the next attempt is the one that counts.
|
|
50
|
+
function placeSubmitted(sidebar, requestId) {
|
|
51
|
+
if (sidebar.editingPlace.length > 0)
|
|
52
|
+
sidebar.editingRequest = requestId
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The answer to that edit: a refused connect keeps the arm, because correcting an address gio could
|
|
56
|
+
// not reach is what the dialog stays open for; ui/shell.qml disarms it when that dialog closes.
|
|
57
|
+
function placeSaved(sidebar, mounts, requestId, uri, success) {
|
|
58
|
+
if (!success || sidebar.editingPlace.length === 0 || requestId !== sidebar.editingRequest)
|
|
59
|
+
return
|
|
60
|
+
var was = sidebar.editingPlace
|
|
61
|
+
sidebar.editingPlace = ""
|
|
62
|
+
sidebar.editingRequest = ""
|
|
63
|
+
if (Mounts.normalize(uri) !== was)
|
|
64
|
+
mounts.replacePlace(was)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// The rail menu's chosen row, handed the row's key rather than its position: the rail rebuilds on
|
|
68
|
+
// a five second poll, so the index the menu opened over can name a different row by now. A key
|
|
69
|
+
// that no longer names a row does nothing, because the row it named has left the rail already.
|
|
70
|
+
// Both Services re-check the kind themselves; this only resolves which row was meant, and the rail
|
|
71
|
+
// itself owns the two rows that need no mount at all.
|
|
72
|
+
function release(action, key, devices, mounts, sidebar) {
|
|
73
|
+
// Flash image hands the pane the volume's whole disk to match against its own drive list.
|
|
74
|
+
if (action === "flashImage") {
|
|
75
|
+
var at = Mounts.rowByKey(sidebar.deviceEntries, key)
|
|
76
|
+
if (at >= 0 && sidebar.navigationPane)
|
|
77
|
+
sidebar.navigationPane.openFlashDrive(sidebar.deviceEntries[at].device)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if (action === "eject") {
|
|
81
|
+
var volume = Mounts.rowByKey(sidebar.deviceEntries, key)
|
|
82
|
+
if (volume >= 0)
|
|
83
|
+
devices.eject(volume)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
// RailAdditions rule 2: Mount and Open are the row's own activation, which mounts when it has to
|
|
87
|
+
// and opens either way, so the menu row and Enter cannot drift apart.
|
|
88
|
+
if (action === "mountVolume" || action === "unmountVolume" || action === "unlockVolume") {
|
|
89
|
+
var row = Mounts.rowByKey(sidebar.deviceEntries, key)
|
|
90
|
+
if (row < 0)
|
|
91
|
+
return
|
|
92
|
+
if (action === "unmountVolume") devices.unmount(row)
|
|
93
|
+
else devices.activate(row)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
// Open is the row's own click: a volume or phone mounts and opens, a share resolves and opens.
|
|
97
|
+
if (action === "open") {
|
|
98
|
+
var volume = Mounts.rowByKey(sidebar.deviceEntries, key)
|
|
99
|
+
if (volume >= 0) {
|
|
100
|
+
devices.activate(volume)
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
var at = Mounts.rowByKey(sidebar.networkEntries, key)
|
|
104
|
+
if (at >= 0) {
|
|
105
|
+
mounts.activate(at, sidebar.navigationPane)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
sidebar.openPhone(key)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
// The phone Service is the sidebar's own child, so the sidebar resolves the key against it; a
|
|
112
|
+
// phone unmounts rather than ejects, because gvfs answers can_eject=0 for the MTP monitor.
|
|
113
|
+
if (action === "unmountPhone") {
|
|
114
|
+
sidebar.releasePhone(key)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
// Mount is the row's own activation, which mounts when it has to and opens either way.
|
|
118
|
+
if (action === "mountPhone") {
|
|
119
|
+
sidebar.openPhone(key)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
var share = Mounts.rowByKey(sidebar.networkEntries, key)
|
|
123
|
+
if (share < 0)
|
|
124
|
+
return
|
|
125
|
+
if (action === "unmount")
|
|
126
|
+
mounts.unmount(share)
|
|
127
|
+
else if (action === "editPlace")
|
|
128
|
+
editPlace(sidebar, share)
|
|
129
|
+
else if (action === "rename")
|
|
130
|
+
sidebar.startRename(sidebar.placesEntries.length + share)
|
|
131
|
+
else if (action === "remove")
|
|
132
|
+
mounts.forget(sidebar.networkEntries[share].uri)
|
|
133
|
+
}
|
package/ui/js/Recent.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "Format.js" as Format
|
|
3
|
+
|
|
4
|
+
// The desktop's own history, read and never written; every bookmark is untrusted input.
|
|
5
|
+
|
|
6
|
+
// The freedesktop recent file, and where XDG_DATA_HOME defaults to when the session did not set it.
|
|
7
|
+
var HISTORY_LEAF = "recently-used.xbel"
|
|
8
|
+
var DEFAULT_DATA_HOME = "/.local/share"
|
|
9
|
+
|
|
10
|
+
// The output loop stops here and hands the rail the newest LIMIT paths.
|
|
11
|
+
var LIMIT = 500
|
|
12
|
+
|
|
13
|
+
// Sample input: ("/home/gm/.local/share", "/home/gm") and ("", "/home/gm"); an XDG_DATA_HOME that
|
|
14
|
+
// is not an absolute path is not a data home, so the default answers for it too.
|
|
15
|
+
function historyPath(dataHome, home) {
|
|
16
|
+
var root = String(dataHome || "")
|
|
17
|
+
if (root.charAt(0) !== "/") {
|
|
18
|
+
root = String(home || "") + DEFAULT_DATA_HOME
|
|
19
|
+
}
|
|
20
|
+
return root.replace(/\/+$/, "") + "/" + HISTORY_LEAF
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Sample input: "file:///home/gm/a%20b.png" becomes "/home/gm/a b.png".
|
|
24
|
+
// Refuses non-file schemes, foreign authorities, bad escapes and control characters.
|
|
25
|
+
function pathOf(href) {
|
|
26
|
+
var raw = String(href || "")
|
|
27
|
+
if (raw.substring(0, 7).toLowerCase() !== "file://") {
|
|
28
|
+
return ""
|
|
29
|
+
}
|
|
30
|
+
var rest = raw.substring(7)
|
|
31
|
+
// The authority is everything before the path's own leading slash.
|
|
32
|
+
var cut = rest.indexOf("/")
|
|
33
|
+
if (cut < 0) {
|
|
34
|
+
return ""
|
|
35
|
+
}
|
|
36
|
+
var authority = rest.substring(0, cut).toLowerCase()
|
|
37
|
+
if (authority.length > 0 && authority !== "localhost") {
|
|
38
|
+
return ""
|
|
39
|
+
}
|
|
40
|
+
var decoded = ""
|
|
41
|
+
try {
|
|
42
|
+
decoded = decodeURIComponent(rest.substring(cut))
|
|
43
|
+
} catch (e) {
|
|
44
|
+
return ""
|
|
45
|
+
}
|
|
46
|
+
if (decoded.charAt(0) !== "/" || decoded === "/") {
|
|
47
|
+
return ""
|
|
48
|
+
}
|
|
49
|
+
// A control character cannot appear in a path this window will draw or send back as a URI.
|
|
50
|
+
if (/[\x00-\x1f\x7f]/.test(decoded)) {
|
|
51
|
+
return ""
|
|
52
|
+
}
|
|
53
|
+
return decoded
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Sample bookmarks: [{ href: "file:///home/gm/a.txt", stamp: "2026-08-30T11:32:04Z" }].
|
|
57
|
+
// Newest first with the stamps kept, so the Used column draws the visit, not the mtime.
|
|
58
|
+
function entries(bookmarks) {
|
|
59
|
+
var rows = []
|
|
60
|
+
for (var i = 0; i < bookmarks.length; i++) {
|
|
61
|
+
var path = pathOf(bookmarks[i].href)
|
|
62
|
+
if (path.length === 0) {
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
rows.push({ path: path, stamp: String(bookmarks[i].stamp || ""), at: rows.length })
|
|
66
|
+
}
|
|
67
|
+
// The file's own order is the tie-break, so two bookmarks sharing a stamp never swap between reads.
|
|
68
|
+
rows.sort(function (a, b) {
|
|
69
|
+
if (a.stamp === b.stamp) {
|
|
70
|
+
return a.at - b.at
|
|
71
|
+
}
|
|
72
|
+
return a.stamp < b.stamp ? 1 : -1
|
|
73
|
+
})
|
|
74
|
+
var out = []
|
|
75
|
+
var seen = {}
|
|
76
|
+
for (var j = 0; j < rows.length && out.length < LIMIT; j++) {
|
|
77
|
+
if (seen[rows[j].path]) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
seen[rows[j].path] = true
|
|
81
|
+
out.push({ path: rows[j].path, stamp: rows[j].stamp })
|
|
82
|
+
}
|
|
83
|
+
return out
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Sample input: "/home/gm/a.txt" names "a.txt" in "/home/gm".
|
|
87
|
+
// The leaf draws as the name and its parent as the caption beside it.
|
|
88
|
+
function nameOf(path) {
|
|
89
|
+
var text = String(path || "")
|
|
90
|
+
var cut = text.lastIndexOf("/")
|
|
91
|
+
return cut < 0 ? text : text.substring(cut + 1)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sample input: "home/gm/a.txt" sits in "/home/gm", and "a.txt" in "/".
|
|
95
|
+
function locationOf(path) {
|
|
96
|
+
var text = String(path || "")
|
|
97
|
+
if (text.length === 0) {
|
|
98
|
+
return ""
|
|
99
|
+
}
|
|
100
|
+
var cut = text.lastIndexOf("/")
|
|
101
|
+
if (cut <= 0) {
|
|
102
|
+
return "/"
|
|
103
|
+
}
|
|
104
|
+
var parent = text.substring(0, cut)
|
|
105
|
+
return parent.charAt(0) === "/" ? parent : "/" + parent
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Sample input: "home/gm/Documents/claude/a.md" under "/home/gm" is "Documents/claude", a file in home "~", outside it absolute.
|
|
109
|
+
function locationUnder(path, home) {
|
|
110
|
+
var shown = Format.tilde(locationOf(path), String(home || ""))
|
|
111
|
+
if (shown.indexOf("~/") === 0) {
|
|
112
|
+
return shown.substring(2)
|
|
113
|
+
}
|
|
114
|
+
return shown
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// One bounded lookup accompanies the entries; no file stat or extra history read.
|
|
118
|
+
function visits(entries) {
|
|
119
|
+
var out = {}
|
|
120
|
+
for (var i = 0; i < entries.length; i++) {
|
|
121
|
+
var seconds = Date.parse(entries[i].stamp) / 1000
|
|
122
|
+
out[entries[i].path] = isFinite(seconds) ? seconds : null
|
|
123
|
+
}
|
|
124
|
+
return out
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Each asker waits once, and null, the rail pane itself, joins like any other.
|
|
128
|
+
function joinRequesters(current, requester) {
|
|
129
|
+
var asker = requester || null
|
|
130
|
+
var seen = current || []
|
|
131
|
+
if (seen.indexOf(asker) >= 0) {
|
|
132
|
+
return seen
|
|
133
|
+
}
|
|
134
|
+
return seen.concat([asker])
|
|
135
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "BackReturn.js" as BackReturn
|
|
4
|
+
.import "DirSizes.js" as DirSizes
|
|
5
|
+
.import "Nav.js" as Nav
|
|
6
|
+
.import "Search.js" as Search
|
|
7
|
+
.import "Thumbs.js" as Thumbs
|
|
8
|
+
|
|
9
|
+
// Recent lists the history's paths under "/", newest first via mtime desc.
|
|
10
|
+
var OFF = ""
|
|
11
|
+
var RESULTS = "results"
|
|
12
|
+
|
|
13
|
+
// One refusal for both paste routes: the menu reaches past key dispatch, so it shares this.
|
|
14
|
+
function refusePaste(pane) {
|
|
15
|
+
if (pane.recentMode.length > 0) {
|
|
16
|
+
pane.message((pane.recentMode === "trash" ? "Trash" : "Recent") + " is a collection. Open a folder before pasting", false)
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Open keeps where the pane stood and asks the history's paths after the bounded jump; spanning mounts leaves fsinfo unknown.
|
|
23
|
+
function run(pane, paths, visits, mode) {
|
|
24
|
+
// The history takes the place of a listing out; its answer is dropped by the folder it names, see ui/js/Swap.js onListed.
|
|
25
|
+
if (pane.listInFlight) {
|
|
26
|
+
pane.swap.stopWaiting()
|
|
27
|
+
BackReturn.dropOwed(pane)
|
|
28
|
+
}
|
|
29
|
+
// The standing order is kept for the way back, not the mtime it replaces.
|
|
30
|
+
if (pane.recentMode.length === 0 && pane.backend) {
|
|
31
|
+
pane.recentSortBy = pane.backend.sortBy
|
|
32
|
+
pane.recentSortDesc = pane.backend.sortDesc === true
|
|
33
|
+
}
|
|
34
|
+
if (pane.recentFrom.length === 0) {
|
|
35
|
+
pane.recentFrom = Nav.place(pane)
|
|
36
|
+
}
|
|
37
|
+
// A history replaces the standing listing, so a live session closes first.
|
|
38
|
+
Search.leaveWalk(pane)
|
|
39
|
+
// The paths this listing stands on, so a refresh with the rail hidden still re-asks them.
|
|
40
|
+
pane.recentPaths = paths || []
|
|
41
|
+
pane.recentVisits = visits || ({})
|
|
42
|
+
pane.listInFlight = true
|
|
43
|
+
pane.listedSeen = false
|
|
44
|
+
pane.path = "/"
|
|
45
|
+
pane.recentMode = mode || RESULTS
|
|
46
|
+
pane.listingPath = "/"
|
|
47
|
+
pane.storageClass = ""
|
|
48
|
+
// Recent rows span filesystems, so the Tags row stays and the backend refuses each filesystem that cannot hold tags by name.
|
|
49
|
+
pane.canTag = true
|
|
50
|
+
pane.fsName = ""
|
|
51
|
+
pane.fsFree = 0
|
|
52
|
+
// Recent asks for no figures, so its empty slot is an answer.
|
|
53
|
+
pane.fsAnswered = true
|
|
54
|
+
var ask = {}
|
|
55
|
+
// A walk closed above keeps its rows and its strip until the history lands.
|
|
56
|
+
if (pane.searchLeaving === true) ask.holdWalk = true
|
|
57
|
+
Nav.holdOrForget(pane, ask)
|
|
58
|
+
pane.appliedListingPreferences = pane.listingPreferences
|
|
59
|
+
if (pane.recentMode === "trash") pane.backend.send({c:"listtrash",first:pane.windowSize})
|
|
60
|
+
else pane.backend.listPaths(pane.recentPaths, pane.windowSize)
|
|
61
|
+
// A deep anchored refresh asks its window only after the history's listpaths request.
|
|
62
|
+
var anchor = pane.wire ? pane.wire.anchor : null
|
|
63
|
+
if (anchor && anchor.path === pane.path && anchor.start > 0)
|
|
64
|
+
pane.backend.window(anchor.start, pane.windowSize)
|
|
65
|
+
pane.backend.sortBy = "mtime"
|
|
66
|
+
pane.backend.sortDesc = true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Leaving Recent re-lists the folder it was opened over, pushing no history entry.
|
|
70
|
+
function close(pane) {
|
|
71
|
+
var back = pane.recentFrom.length > 0 ? pane.recentFrom : "/"
|
|
72
|
+
leave(pane)
|
|
73
|
+
pane.openWithoutHistory(back, { supersede: true })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// The order run() replaced, handed back before the folder re-lists.
|
|
77
|
+
function restoreSort(pane) {
|
|
78
|
+
if (pane.backend && typeof pane.recentSortBy === "string" && pane.recentSortBy.length > 0) {
|
|
79
|
+
pane.backend.sortBy = pane.recentSortBy
|
|
80
|
+
pane.backend.sortDesc = pane.recentSortDesc === true
|
|
81
|
+
}
|
|
82
|
+
pane.recentSortBy = ""
|
|
83
|
+
pane.recentSortDesc = false
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Every way out of Recent, a plain hop, Escape, a tab switch and reveal, leaves through this one step, so each restores the same order.
|
|
87
|
+
function leave(pane) {
|
|
88
|
+
if (pane.recentMode.length > 0) {
|
|
89
|
+
restoreSort(pane)
|
|
90
|
+
}
|
|
91
|
+
pane.recentMode = OFF
|
|
92
|
+
pane.recentFrom = ""
|
|
93
|
+
pane.recentPaths = []
|
|
94
|
+
pane.recentVisits = ({})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Refresh re-reads history after an operation, never re-listing "/" over it; an unloaded sidebar re-asks the standing paths.
|
|
98
|
+
function refresh(pane, selectPath) {
|
|
99
|
+
// A re-ask of the history is no navigation, so it never takes the place of a listing out.
|
|
100
|
+
if (pane.listInFlight) {
|
|
101
|
+
pane.message("A directory is already loading.", false)
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
pane.pendingSelect = selectPath ? selectPath : ""
|
|
105
|
+
pane.pendingMenu = false
|
|
106
|
+
if (pane.recentMode === "trash") { run(pane, [], {}, "trash"); return }
|
|
107
|
+
if (pane.sidebar) {
|
|
108
|
+
pane.sidebar.readRecent(pane)
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
run(pane, pane.recentPaths || [], pane.recentVisits)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Only the held viewport rows gain a Used stamp; filesystem metadata remains intact.
|
|
115
|
+
function stampRows(pane, rows) {
|
|
116
|
+
if (pane.recentMode !== RESULTS) return rows
|
|
117
|
+
return rows.map(function (row) {
|
|
118
|
+
var visit = (pane.recentVisits || {})[pane.join(pane.path, row.n)]
|
|
119
|
+
return Object.assign({}, row, { used: visit === undefined ? null : visit })
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The folder a tab records on a history, "" elsewhere, so it lands where it stood.
|
|
124
|
+
function restingPath(pane) {
|
|
125
|
+
return pane.recentMode.length > 0 && pane.recentFrom.length > 0 ? pane.recentFrom : ""
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// A tab switch drops the mode and says whether it dropped one.
|
|
129
|
+
function dropOverlay(pane) {
|
|
130
|
+
if (pane.recentMode.length === 0) {
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
leave(pane)
|
|
134
|
+
return true
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// o opens the row's own folder; Enter opens the file itself, so the two cannot disagree.
|
|
138
|
+
function reveal(pane) {
|
|
139
|
+
if (pane.listInFlight) {
|
|
140
|
+
pane.message("A directory is already loading.", false)
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
var row = pane.rowFor(pane.cursorIndex)
|
|
144
|
+
if (!row) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
var full = pane.join(pane.path, row.n)
|
|
148
|
+
var cut = full.lastIndexOf("/")
|
|
149
|
+
if (cut < 0) {
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
var dir = cut === 0 ? "/" : full.substring(0, cut)
|
|
153
|
+
leave(pane)
|
|
154
|
+
pane.pendingSelect = full
|
|
155
|
+
pane.openWithoutHistory(dir)
|
|
156
|
+
}
|
package/ui/js/Reload.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Anchor.js" as Anchor
|
|
4
|
+
.import "Format.js" as Format
|
|
5
|
+
.import "Nav.js" as Nav
|
|
6
|
+
.import "Swap.js" as Swap
|
|
7
|
+
|
|
8
|
+
// The status line a reload answers with, one whole sentence per count, each filled by Format.fill.
|
|
9
|
+
var WORDS = {
|
|
10
|
+
one: "Reloaded · 1 row changed",
|
|
11
|
+
many: "Reloaded · %1 rows changed"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Sample input: 2 is "Reloaded · 2 rows changed", 1 is "Reloaded · 1 row changed".
|
|
15
|
+
function line(changed) {
|
|
16
|
+
return changed === 1 ? WORDS.one : Format.fill(WORDS.many, Format.count(changed))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// A listing out refuses itself and a search owns its header, so both go quiet.
|
|
20
|
+
function begin(pane, wire) {
|
|
21
|
+
if (Nav.loading(pane)) {
|
|
22
|
+
pane.message(Swap.LOADING, false)
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
if (pane.searchMode.length > 0)
|
|
26
|
+
return false
|
|
27
|
+
// A reload past the wait retries the folder waited on, which a first listing has not yet made the pane's path.
|
|
28
|
+
var waitedOn = pane.listingState === "waiting" ? Nav.trimSlash(pane.listingPath || "") : ""
|
|
29
|
+
// A reload that lists lets a waiting listing go first, the way ui/Pane.qml openWithoutHistory does.
|
|
30
|
+
Anchor.clearWaiting(pane)
|
|
31
|
+
// Recent re-reads its history rather than re-listing its base, which is the root.
|
|
32
|
+
if (pane.recentMode.length > 0) {
|
|
33
|
+
pane.refresh("")
|
|
34
|
+
} else {
|
|
35
|
+
if (waitedOn.length > 0 && waitedOn !== pane.path) {
|
|
36
|
+
pane.openWithoutHistory(waitedOn)
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
wire.anchor = Anchor.watched(pane, true, pane.anchorRowHeight)
|
|
40
|
+
}
|
|
41
|
+
// Set after the anchored re-read above, because opening the listing clears it first.
|
|
42
|
+
pane.reloadFrom = pane.total
|
|
43
|
+
pane.reloadChanged = -1
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Runs on every rows reply; only a manual reload owes a notice, said whenever rows changed.
|
|
48
|
+
function landed(pane) {
|
|
49
|
+
if (pane.reloadFrom < 0)
|
|
50
|
+
return ""
|
|
51
|
+
// The backend counts added plus removed on a same-path re-list; a missing one falls back to net delta.
|
|
52
|
+
var changed = pane.reloadChanged >= 0 ? pane.reloadChanged : Math.abs(pane.total - pane.reloadFrom)
|
|
53
|
+
pane.reloadFrom = -1
|
|
54
|
+
pane.reloadChanged = -1
|
|
55
|
+
if (changed === 0)
|
|
56
|
+
return ""
|
|
57
|
+
var text = line(changed)
|
|
58
|
+
pane.message(text, false)
|
|
59
|
+
return text
|
|
60
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// Only src/gui.rs's own implicit Vulkan choice may retry, and the argv is what it relaunches Flea with.
|
|
4
|
+
// Sample input: ("vulkan", "1", "/usr/bin/flea"), and ("opengl", "1", "/usr/bin/flea") for a launch that may not.
|
|
5
|
+
function fallbackCommand(backend, automatic, bin) {
|
|
6
|
+
if (backend !== "vulkan" || automatic !== "1")
|
|
7
|
+
return null
|
|
8
|
+
return ["/usr/bin/env", "QSG_RHI_BACKEND=opengl", bin || "flea", "--gui"]
|
|
9
|
+
}
|
package/ui/js/RunFile.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
.import "Anchor.js" as Anchor
|
|
5
|
+
.import "Icons.js" as Icons
|
|
6
|
+
|
|
7
|
+
// The run card's own decisions, the way ui/js/Collide.js holds its card's: which buttons a kind draws, where the ring opens, and how the keys walk them.
|
|
8
|
+
|
|
9
|
+
var LABELS = { cancel: "Cancel", text: "Open as text", run: "Run", terminal: "Run in terminal" }
|
|
10
|
+
// Run in terminal is the default because it shows the output; a desktop handler, where one is named, is offered first and takes it instead.
|
|
11
|
+
var START = "terminal"
|
|
12
|
+
var KINDS = ["script", "elf", "appimage"]
|
|
13
|
+
var KIND_LABELS = { script: "Shell script", elf: "Program", appimage: "AppImage application bundle" }
|
|
14
|
+
|
|
15
|
+
// The board's own default: the handler where the desktop names one for an AppImage, Run in terminal everywhere else, and the primary frame sits there.
|
|
16
|
+
function defaultButton(kind, hasHandler) {
|
|
17
|
+
if (kind === "appimage" && hasHandler === true)
|
|
18
|
+
return "handler"
|
|
19
|
+
return START
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Left to right, the board's own order, text a script's alone since a binary is not text; the board's tweak focus = terminal | run | text | cancel names the opening ring.
|
|
23
|
+
function buttonsFor(kind, hasHandler) {
|
|
24
|
+
var out = ["cancel"]
|
|
25
|
+
if (kind === "script")
|
|
26
|
+
out.push("text")
|
|
27
|
+
if (kind === "appimage" && hasHandler === true)
|
|
28
|
+
out.push("handler")
|
|
29
|
+
return out.concat(["run", "terminal"])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// A tweak naming a button the card did not draw falls back to the default, never to nothing.
|
|
33
|
+
function startFocus(buttons, tweak) {
|
|
34
|
+
if (buttons.indexOf(tweak) >= 0)
|
|
35
|
+
return tweak
|
|
36
|
+
var fallback = defaultButton(buttons.indexOf("handler") >= 0 ? "appimage" : "script", buttons.indexOf("handler") >= 0)
|
|
37
|
+
return buttons.indexOf(fallback) >= 0 ? fallback : buttons[0]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// h and l stop at the ends like TrashConfirm's pair; Tab and Backtab come round.
|
|
41
|
+
function moved(buttons, focus, key, modifiers) {
|
|
42
|
+
var at = buttons.indexOf(focus)
|
|
43
|
+
if (at < 0)
|
|
44
|
+
at = buttons.indexOf(defaultButton(buttons.indexOf("handler") >= 0 ? "appimage" : "script", buttons.indexOf("handler") >= 0))
|
|
45
|
+
if (at < 0)
|
|
46
|
+
at = 0
|
|
47
|
+
var last = buttons.length - 1
|
|
48
|
+
if (key === Qt.Key_H || key === Qt.Key_Left)
|
|
49
|
+
return buttons[Math.max(0, at - 1)]
|
|
50
|
+
if (key === Qt.Key_L || key === Qt.Key_Right)
|
|
51
|
+
return buttons[Math.min(last, at + 1)]
|
|
52
|
+
// Shift+Tab reaches a Keys handler as Backtab or as Tab with Shift held, depending on the input path.
|
|
53
|
+
if (key === Qt.Key_Backtab || key === Qt.Key_Tab && (modifiers & Qt.ShiftModifier))
|
|
54
|
+
return buttons[(at + last) % buttons.length]
|
|
55
|
+
if (key === Qt.Key_Tab)
|
|
56
|
+
return buttons[(at + 1) % buttons.length]
|
|
57
|
+
return buttons[at]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The handler button is the application's own Name= from its desktop entry, one whole string.
|
|
61
|
+
function labelFor(button, handlerName) {
|
|
62
|
+
return button === "handler" ? String(handlerName) : LABELS[button]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function activates(key) {
|
|
66
|
+
return key === Qt.Key_Return || key === Qt.Key_Enter || key === Qt.Key_Space
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The name row's own mark: the board draws the code glyph beside a script, and a binary takes the mark its listed row draws.
|
|
70
|
+
var KIND_GLYPHS = { script: "code", elf: "terminal", appimage: "app-window" }
|
|
71
|
+
|
|
72
|
+
function glyphFor(kind, listed) {
|
|
73
|
+
if (kind !== "script" && listed)
|
|
74
|
+
return listed
|
|
75
|
+
return KIND_GLYPHS[kind] || KIND_GLYPHS.elf
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The mark the listing draws for the row the ask was opened from, or "" when no listed row names that path.
|
|
79
|
+
function listedGlyph(pane, path) {
|
|
80
|
+
if (!pane || !pane.rows || typeof pane.rowFor !== "function")
|
|
81
|
+
return ""
|
|
82
|
+
var at = Anchor.matchListed(pane, path)
|
|
83
|
+
var row = at >= 0 ? pane.rowFor(at) : null
|
|
84
|
+
return row ? Icons.glyphForRow(row.i, row.p) : ""
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Every user-facing string is one whole template filled by Format.fill, never fragments joined with +.
|
|
88
|
+
|
|
89
|
+
// The card's title names the program; the name row carries its kind and its size.
|
|
90
|
+
var TITLE_PROGRAM = "%1 is a program"
|
|
91
|
+
function titleFor(name) {
|
|
92
|
+
return Format.fill(TITLE_PROGRAM, escapeName(name))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// R4: control and bidi characters are shown escaped, never raw, and a clean name passes through byte for byte.
|
|
96
|
+
var SPOOFED = /[\0-\x1F\x7F-\x9F\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069\uFEFF]/g
|
|
97
|
+
function escapeName(name) {
|
|
98
|
+
return String(name).replace(SPOOFED, function (ch) {
|
|
99
|
+
var point = ch.codePointAt(0).toString(16).toUpperCase()
|
|
100
|
+
return "<U+" + ("0000" + point).slice(-4) + ">"
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var SUBTITLE_PROGRAM = "%1 · %2"
|
|
105
|
+
function subtitleFor(kind, bytes) {
|
|
106
|
+
return Format.fill(SUBTITLE_PROGRAM, KIND_LABELS[kind] || KIND_LABELS.elf, Format.size(bytes))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function leafOf(path) {
|
|
110
|
+
var text = String(path)
|
|
111
|
+
var cut = text.lastIndexOf("/")
|
|
112
|
+
return cut < 0 ? text : text.substring(cut + 1)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Sample input: {"ask":"run","kind":"script","size":9420,"handlerId":"","handlerName":"","dev":"8","ino":"9","mtimeS":11,"mtimeNs":12,"target":"/real/tool.sh"}.
|
|
116
|
+
function parseAsk(text) {
|
|
117
|
+
var message = null
|
|
118
|
+
try {
|
|
119
|
+
message = JSON.parse(String(text))
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
if (!message || message.ask !== "run" || KINDS.indexOf(message.kind) < 0)
|
|
124
|
+
return null
|
|
125
|
+
if (typeof message.dev !== "string" || typeof message.ino !== "string" || typeof message.target !== "string")
|
|
126
|
+
return null
|
|
127
|
+
var handlerId = typeof message.handlerId === "string" ? message.handlerId : ""
|
|
128
|
+
var handlerName = typeof message.handlerName === "string" ? message.handlerName : ""
|
|
129
|
+
return { kind: message.kind, size: Number(message.size) || 0,
|
|
130
|
+
handlerId: handlerId, handlerName: handlerName,
|
|
131
|
+
hasHandler: handlerId.length > 0 && handlerName.trim().length > 0,
|
|
132
|
+
dev: message.dev, ino: message.ino,
|
|
133
|
+
mtimeS: Number(message.mtimeS) || 0, mtimeNs: Number(message.mtimeNs) || 0,
|
|
134
|
+
target: message.target }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// The card's choice on the path it asked about, carrying the R3 identity the ask named, and the handler the id the card drew.
|
|
138
|
+
function choose(pane, card, choice) {
|
|
139
|
+
if (choice === "run") card.run(card.path, card.dev, card.ino, card.size, card.mtimeS, card.mtimeNs)
|
|
140
|
+
else if (choice === "terminal") card.runTerminal(card.path, card.dev, card.ino, card.size, card.mtimeS, card.mtimeNs)
|
|
141
|
+
else if (choice === "text") card.openText(card.path, pane)
|
|
142
|
+
else if (choice === "handler") card.openHandler(card.path, card.handlerId, card.dev, card.ino, card.size, card.mtimeS, card.mtimeNs)
|
|
143
|
+
}
|
package/ui/js/Scripts.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// Whole sentences with "%1" for a value, filled by fill so each translates as one.
|
|
6
|
+
var WORDS = {
|
|
7
|
+
exited: "%1 exited with status %2"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// MenuAdditions rule 2's own reading: what find printed becomes the submenu's rows, sorted by name,
|
|
11
|
+
// each label the file name without its extension. Sample input, one name a line:
|
|
12
|
+
// convert-to-webp
|
|
13
|
+
// ocr.sh
|
|
14
|
+
function parse(text, directory) {
|
|
15
|
+
var names = String(text || "").split("\n").filter(function (name) { return name.trim().length > 0 })
|
|
16
|
+
names.sort()
|
|
17
|
+
return names.map(function (name) {
|
|
18
|
+
var trimmed = name.trim()
|
|
19
|
+
var dot = trimmed.lastIndexOf(".")
|
|
20
|
+
return { id: trimmed, label: dot > 0 ? trimmed.substring(0, dot) : trimmed, path: directory + "/" + trimmed }
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The script's own report: its last stderr line, or the status it exited with when it said nothing.
|
|
25
|
+
function failure(name, stderr, code) {
|
|
26
|
+
var lines = String(stderr || "").split("\n").filter(function (line) { return line.trim().length > 0 })
|
|
27
|
+
return lines.length > 0 ? name + " · " + lines[lines.length - 1].trim() : Format.fill(WORDS.exited, name, code)
|
|
28
|
+
}
|