flea-qml 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +511 -2
- package/THIRD-PARTY-NOTICES.md +29 -0
- package/demos/App.java +5 -0
- package/demos/Dockerfile +4 -0
- package/demos/Main.kt +3 -0
- package/demos/Program.cs +1 -0
- package/demos/README.md +12 -0
- package/demos/app.dart +3 -0
- package/demos/app.deb +0 -0
- package/demos/app.js +1 -0
- package/demos/backup.tar +0 -0
- package/demos/budget.xlsx +0 -0
- package/demos/build.sh +2 -0
- package/demos/bundle.zip +0 -0
- package/demos/clip.mp4 +0 -0
- package/demos/data.sqlite +0 -0
- package/demos/disk.iso +0 -0
- package/demos/engine.cpp +5 -0
- package/demos/firmware.bin +0 -0
- package/demos/flake.nix +4 -0
- package/demos/folder/inside.txt +1 -0
- package/demos/font.ttf +0 -0
- package/demos/index.html +5 -0
- package/demos/index.php +2 -0
- package/demos/init.lua +1 -0
- package/demos/letter.docx +0 -0
- package/demos/lib.rs +3 -0
- package/demos/main.c +6 -0
- package/demos/main.h +3 -0
- package/demos/math-and-diagrams.md +200 -0
- package/demos/notes.tar.gz +0 -0
- package/demos/notes.txt +3 -0
- package/demos/photo.png +0 -0
- package/demos/program +2 -0
- package/demos/report.pdf +32 -0
- package/demos/script.py +1 -0
- package/demos/server.go +7 -0
- package/demos/settings.json +4 -0
- package/demos/slides.pptx +0 -0
- package/demos/song.mp3 +0 -0
- package/demos/style.css +4 -0
- package/demos/tool.rb +1 -0
- package/demos/types.ts +2 -0
- package/docs/pele.md +23 -0
- package/docs/protocol.md +3420 -0
- package/docs/temml.md +63 -0
- package/index.js +107 -0
- package/package.json +34 -4
- package/src/archive.js +407 -0
- package/src/archiveops.js +728 -0
- package/src/backend.js +369 -0
- package/src/bulkrename.js +95 -0
- package/src/clip-host.js +76 -0
- package/src/clip.js +462 -0
- package/src/code.js +168 -0
- package/src/compare.js +74 -0
- package/src/create.js +128 -0
- package/src/dirsize.js +147 -0
- package/src/figure-svg.js +530 -0
- package/src/figure-thread.js +23 -0
- package/src/figure.js +132 -0
- package/src/find.js +55 -0
- package/src/flea.js +39 -0
- package/src/fsinfo.js +39 -0
- package/src/highlight.js +141 -0
- package/src/html.js +52 -0
- package/src/kinds.js +89 -0
- package/src/listing.js +130 -0
- package/src/markdown-html.js +317 -0
- package/src/mathml.js +186 -0
- package/src/menu.js +180 -0
- package/src/meta.js +135 -0
- package/src/office.js +810 -0
- package/src/order.js +100 -0
- package/src/peek.js +49 -0
- package/src/pele-wrapper.js +695 -0
- package/src/permissions.js +180 -0
- package/src/places.js +313 -0
- package/src/preview-language.js +10 -0
- package/src/terminal-server.js +95 -0
- package/src/terminal-shell.js +26 -0
- package/src/transfer.js +669 -0
- package/src/trash-record.js +41 -0
- package/src/trash.js +210 -0
- package/src/trashbrowse.js +327 -0
- package/src/uistate.js +393 -0
- package/src/uistore.js +168 -0
- package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
- package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
- package/src/vendor/LICENSES/MIT-katex.txt +21 -0
- package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
- package/src/vendor/LICENSES/MIT-pele.txt +40 -0
- package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
- package/src/vendor/LICENSES/MIT-temml.txt +21 -0
- package/src/vendor/pele.js +145 -0
- package/src/vendor/temml.mjs +14708 -0
- package/src/watch.js +98 -0
- package/src/wayland/client.js +109 -0
- package/src/wayland/protocol.js +55 -0
- package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
- package/src/wayland/protocols/wayland.xml +3248 -0
- package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
- package/src/wayland/resource-io.js +6 -0
- package/src/wayland/socket-discovery.js +26 -0
- package/src/wayland/transport-posix.js +120 -0
- package/src/wayland/transport-win32.js +11 -0
- package/src/wayland/transport.js +3 -0
- package/src/wayland/win32-resources.js +80 -0
- package/src/wayland/win32-wire.js +62 -0
- package/test/archive.test.js +80 -0
- package/test/bench-markdown-scroll.mjs +190 -0
- package/test/clip-host.test.js +113 -0
- package/test/code-preview.test.js +35 -0
- package/test/compare-ui-paths.test.js +24 -0
- package/test/compare.test.js +54 -0
- package/test/delete-trash.test.js +67 -0
- package/test/dirsize.test.js +138 -0
- package/test/file-ops.test.js +307 -0
- package/test/file-url.test.js +24 -0
- package/test/find-contents.test.js +161 -0
- package/test/find-input.test.js +66 -0
- package/test/fixtures/pele-parity.json +1006 -0
- package/test/highlight-launcher.test.js +35 -0
- package/test/html-preview.test.js +22 -0
- package/test/input.test.js +15 -0
- package/test/keymap.test.js +26 -0
- package/test/language-icons.test.js +36 -0
- package/test/launcher.test.js +51 -0
- package/test/manual-archive-here-ui.mjs +47 -0
- package/test/manual-code-scroll.mjs +28 -0
- package/test/manual-column-scroll.mjs +24 -0
- package/test/manual-compare-ui.mjs +20 -0
- package/test/manual-find-status.mjs +37 -0
- package/test/manual-folder-focus.mjs +63 -0
- package/test/manual-html-security.mjs +23 -0
- package/test/manual-html-ui.mjs +30 -0
- package/test/manual-syntax-preview.mjs +26 -0
- package/test/manual-terminal-lifecycle.mjs +42 -0
- package/test/manual-terminal-navigation.mjs +48 -0
- package/test/manual-terminal-ui.mjs +47 -0
- package/test/manual-trash-ui.mjs +86 -0
- package/test/markdown-diagrams.test.js +93 -0
- package/test/markdown-html.test.js +105 -0
- package/test/mathml.test.js +92 -0
- package/test/menu-transfer.test.js +57 -0
- package/test/office-preview.test.js +154 -0
- package/test/pele-parity.test.js +28 -0
- package/test/places.test.js +134 -0
- package/test/preview-keys.test.js +60 -0
- package/test/preview-language.test.js +22 -0
- package/test/status-filename.test.js +20 -0
- package/test/transfer-replace-merge.test.js +187 -0
- package/test/trash-address.test.js +23 -0
- package/test/trash-view.test.js +37 -0
- package/test/trashbrowse.test.js +135 -0
- package/ui/AboutFacts.qml +78 -0
- package/ui/ArchiveCards.qml +95 -0
- package/ui/ArchiveCompressDialog.qml +378 -0
- package/ui/ArchiveHereConfirm.qml +127 -0
- package/ui/ArchivePasswordDialog.qml +257 -0
- package/ui/Backend.qml +775 -0
- package/ui/BulkOldNames.qml +47 -0
- package/ui/BulkRenameDialog.qml +380 -0
- package/ui/CardFrame.qml +67 -0
- package/ui/CardScroll.qml +79 -0
- package/ui/CheckBox.qml +73 -0
- package/ui/ChromeBar.qml +360 -0
- package/ui/ChromeButton.qml +131 -0
- package/ui/ChromeModes.qml +43 -0
- package/ui/CodeLines.qml +256 -0
- package/ui/CodeRefusal.qml +52 -0
- package/ui/CollideConfirm.qml +147 -0
- package/ui/CollideHost.qml +167 -0
- package/ui/ColumnPane.qml +525 -0
- package/ui/ColumnRow.qml +380 -0
- package/ui/ColumnsArea.qml +594 -0
- package/ui/ColumnsLanding.qml +147 -0
- package/ui/CompareView.qml +321 -0
- package/ui/ContextMenu.qml +743 -0
- package/ui/ConvertDialog.qml +456 -0
- package/ui/CountedWorker.qml +7 -0
- package/ui/Crumb.qml +64 -0
- package/ui/CrumbDrop.qml +25 -0
- package/ui/CsvTable.qml +394 -0
- package/ui/CursorMark.qml +41 -0
- package/ui/DefaultClaim.qml +130 -0
- package/ui/DeviceLock.qml +47 -0
- package/ui/DeviceMounts.qml +540 -0
- package/ui/DialogButton.qml +150 -0
- package/ui/DialogField.qml +147 -0
- package/ui/DialogHeader.qml +31 -0
- package/ui/DialogScrim.qml +20 -0
- package/ui/DialogTitle.qml +37 -0
- package/ui/Divider.qml +9 -0
- package/ui/DropInto.qml +126 -0
- package/ui/DropTarget.qml +35 -0
- package/ui/DropboxMark.qml +60 -0
- package/ui/EmptyState.qml +150 -0
- package/ui/EscapeHint.qml +11 -0
- package/ui/FactsTable.qml +54 -0
- package/ui/FastScrollHandler.qml +476 -0
- package/ui/FatNamesConfirm.qml +157 -0
- package/ui/Favourites.qml +143 -0
- package/ui/FigureService.qml +120 -0
- package/ui/FileDrag.qml +267 -0
- package/ui/FilterStrip.qml +133 -0
- package/ui/FitMetrics.qml +22 -0
- package/ui/FlashDriveRows.qml +137 -0
- package/ui/FlashHost.qml +72 -0
- package/ui/FlashImageRow.qml +104 -0
- package/ui/FlashRunner.qml +119 -0
- package/ui/FlashStepRow.qml +172 -0
- package/ui/FlashStrip.qml +38 -0
- package/ui/FlashView.qml +397 -0
- package/ui/FleaMark.qml +88 -0
- package/ui/FlyoutFade.qml +23 -0
- package/ui/FontPane.qml +170 -0
- package/ui/Glass.qml +114 -0
- package/ui/GlassTagger.qml +59 -0
- package/ui/Glyph.qml +64 -0
- package/ui/GlyphSolid.qml +22 -0
- package/ui/GridArea.qml +664 -0
- package/ui/GridTile.qml +325 -0
- package/ui/GvfsBridge.qml +164 -0
- package/ui/Header.qml +433 -0
- package/ui/HoverTimers.qml +39 -0
- package/ui/HtmlClip.qml +30 -0
- package/ui/HtmlPane.qml +63 -0
- package/ui/HyprlandMark.qml +45 -0
- package/ui/Ipc.qml +1005 -0
- package/ui/IpcControls.qml +7 -0
- package/ui/IpcMenuState.qml +48 -0
- package/ui/IpcPreviewState.qml +185 -0
- package/ui/JumpPath.qml +51 -0
- package/ui/KeymapSheet.qml +561 -0
- package/ui/KeymapSheetResult.qml +89 -0
- package/ui/KindStack.qml +40 -0
- package/ui/LangMark.qml +51 -0
- package/ui/LangMarkLoader.qml +26 -0
- package/ui/List.qml +480 -0
- package/ui/LoadingState.qml +54 -0
- package/ui/LocalSend.qml +56 -0
- package/ui/LocalSendMark.qml +73 -0
- package/ui/MarkdownBlockView.qml +311 -0
- package/ui/MarkdownBlocks.qml +35 -0
- package/ui/MarkdownFigure.qml +254 -0
- package/ui/MarkdownImages.qml +72 -0
- package/ui/MarkdownList.qml +81 -0
- package/ui/MarkdownMathsText.qml +97 -0
- package/ui/MarkdownPane.qml +83 -0
- package/ui/MarkdownPictures.qml +41 -0
- package/ui/MarkdownQuote.qml +52 -0
- package/ui/MarkdownRemote.qml +82 -0
- package/ui/MarkdownSource.qml +155 -0
- package/ui/MarkdownTable.qml +222 -0
- package/ui/MarkdownTableScroll.qml +64 -0
- package/ui/MarkdownTableWheel.qml +126 -0
- package/ui/MarkdownText.qml +87 -0
- package/ui/MarkdownWorker.js +132 -0
- package/ui/MatchText.qml +99 -0
- package/ui/MediaSession.qml +38 -0
- package/ui/MediaSound.qml +26 -0
- package/ui/MediaStrip.qml +191 -0
- package/ui/MediaTrack.qml +36 -0
- package/ui/MediaVolume.qml +54 -0
- package/ui/MenuActionDialog.qml +396 -0
- package/ui/MenuEdgeFade.qml +24 -0
- package/ui/MenuHost.qml +227 -0
- package/ui/MenuRow.qml +351 -0
- package/ui/MountListing.qml +100 -0
- package/ui/NetworkDialog.qml +435 -0
- package/ui/NetworkForm.qml +426 -0
- package/ui/NetworkMounts.qml +913 -0
- package/ui/NetworkPlaces.qml +127 -0
- package/ui/OpenWithDialog.qml +613 -0
- package/ui/Opener.qml +95 -0
- package/ui/Pane.qml +1320 -0
- package/ui/PaneMenuActions.qml +527 -0
- package/ui/PanePath.qml +90 -0
- package/ui/PaneRail.qml +268 -0
- package/ui/PaneStates.qml +88 -0
- package/ui/PaneSwap.qml +339 -0
- package/ui/PaneWire.qml +601 -0
- package/ui/ParkedWait.qml +9 -0
- package/ui/PathEntry.qml +186 -0
- package/ui/PathJump.qml +400 -0
- package/ui/PdfViewer.qml +297 -0
- package/ui/PermissionsDialog.qml +658 -0
- package/ui/PermissionsHeadings.qml +31 -0
- package/ui/PhoneMounts.qml +67 -0
- package/ui/PickerAnswers.qml +75 -0
- package/ui/PickerBox.qml +26 -0
- package/ui/PickerChrome.qml +295 -0
- package/ui/PickerFilter.qml +97 -0
- package/ui/PickerGrid.qml +308 -0
- package/ui/PickerHeader.qml +28 -0
- package/ui/PickerLifecycle.qml +27 -0
- package/ui/PickerList.qml +218 -0
- package/ui/PickerListing.qml +149 -0
- package/ui/PickerPathBar.qml +127 -0
- package/ui/PickerPlaces.qml +189 -0
- package/ui/PickerRecent.qml +92 -0
- package/ui/PickerSave.qml +101 -0
- package/ui/PickerSelection.qml +155 -0
- package/ui/PickerWindow.qml +846 -0
- package/ui/PowerSectorsReader.qml +10 -0
- package/ui/PrebuildDriver.qml +146 -0
- package/ui/Preview.qml +1005 -0
- package/ui/PreviewAnimation.qml +24 -0
- package/ui/PreviewArchive.qml +91 -0
- package/ui/PreviewColumn.qml +553 -0
- package/ui/PreviewEdge.qml +63 -0
- package/ui/PreviewFacts.qml +30 -0
- package/ui/PreviewFactsLine.qml +34 -0
- package/ui/PreviewHtml.qml +233 -0
- package/ui/PreviewImage.qml +253 -0
- package/ui/PreviewImagePan.qml +13 -0
- package/ui/PreviewImageSharp.qml +32 -0
- package/ui/PreviewLines.qml +106 -0
- package/ui/PreviewMarkdown.qml +706 -0
- package/ui/PreviewMedia.qml +250 -0
- package/ui/PreviewPdf.qml +482 -0
- package/ui/PreviewScroll.qml +201 -0
- package/ui/PreviewSwap.qml +335 -0
- package/ui/PreviewText.qml +149 -0
- package/ui/ProtocolChip.qml +18 -0
- package/ui/QuickLookArchive.qml +41 -0
- package/ui/QuickLookBar.qml +103 -0
- package/ui/QuickLookPrepare.qml +355 -0
- package/ui/QuickLookSwap.qml +10 -0
- package/ui/QuickLookWarm.qml +65 -0
- package/ui/RailDrop.qml +155 -0
- package/ui/RailHeading.qml +19 -0
- package/ui/RailPlaces.qml +38 -0
- package/ui/RailTrashRow.qml +67 -0
- package/ui/RenameField.qml +353 -0
- package/ui/RendererRetry.qml +11 -0
- package/ui/ResizeHandle.qml +52 -0
- package/ui/Reveal.qml +54 -0
- package/ui/Row.qml +476 -0
- package/ui/RowDrag.qml +213 -0
- package/ui/RowDropTarget.qml +27 -0
- package/ui/RunCard.qml +286 -0
- package/ui/Scripts.qml +65 -0
- package/ui/SearchStrip.qml +232 -0
- package/ui/SelectionBand.qml +140 -0
- package/ui/SelectionPreview.qml +370 -0
- package/ui/SettingsFavourite.qml +122 -0
- package/ui/SettingsGroup.qml +96 -0
- package/ui/SettingsHint.qml +12 -0
- package/ui/SettingsMetrics.qml +29 -0
- package/ui/SettingsPane.qml +178 -0
- package/ui/SettingsPanel.qml +547 -0
- package/ui/SettingsRail.qml +128 -0
- package/ui/SettingsRow.qml +409 -0
- package/ui/SettingsRuler.qml +85 -0
- package/ui/SettingsSegment.qml +85 -0
- package/ui/ShareBrowser.qml +113 -0
- package/ui/ShareLink.qml +67 -0
- package/ui/ShelfPins.qml +101 -0
- package/ui/Sidebar.qml +728 -0
- package/ui/SidebarRow.qml +358 -0
- package/ui/Spinner.qml +60 -0
- package/ui/StableModel.qml +89 -0
- package/ui/StateMessage.qml +84 -0
- package/ui/StatusBar.qml +512 -0
- package/ui/StatusFileName.qml +27 -0
- package/ui/TabBar.qml +673 -0
- package/ui/TabDragGeometry.qml +60 -0
- package/ui/TagMenuMark.qml +52 -0
- package/ui/TagRailSection.qml +53 -0
- package/ui/TagSlot.qml +40 -0
- package/ui/TagState.qml +150 -0
- package/ui/TagSwatch.qml +28 -0
- package/ui/Taildrop.qml +86 -0
- package/ui/TailscaleMark.qml +50 -0
- package/ui/TerminalService.qml +69 -0
- package/ui/TerminalTab.qml +48 -0
- package/ui/TerminalTabs.qml +27 -0
- package/ui/Theme.qml +460 -0
- package/ui/ThemeColors.qml +43 -0
- package/ui/TransferCard.qml +239 -0
- package/ui/TrashConfirm.qml +129 -0
- package/ui/TrashDropHandler.qml +11 -0
- package/ui/TrashHost.qml +202 -0
- package/ui/TrashMonitor.qml +30 -0
- package/ui/TrashToolbar.qml +19 -0
- package/ui/TrashView.qml +492 -0
- package/ui/UnlockDialog.qml +450 -0
- package/ui/UnlockHost.qml +206 -0
- package/ui/UpdateCheck.qml +97 -0
- package/ui/UserDirs.qml +25 -0
- package/ui/ViewState.qml +565 -0
- package/ui/ViewportScrollBar.qml +195 -0
- package/ui/ViewportScrollBars.qml +25 -0
- package/ui/WebPreview.qml +33 -0
- package/ui/WebPreviewView.qml +26 -0
- package/ui/WindowBody.qml +829 -0
- package/ui/WindowDrag.qml +35 -0
- package/ui/boot/Commons/AudioNodes.qml +50 -0
- package/ui/boot/Commons/AudioNodesModel.js +96 -0
- package/ui/boot/Commons/Border.qml +243 -0
- package/ui/boot/Commons/BorderGeometry.js +373 -0
- package/ui/boot/Commons/Color.qml +258 -0
- package/ui/boot/Commons/I18n.qml +118 -0
- package/ui/boot/Commons/InputDevices.qml +112 -0
- package/ui/boot/Commons/InputMethodState.qml +38 -0
- package/ui/boot/Commons/IpcRegistry.qml +77 -0
- package/ui/boot/Commons/Numbers.js +19 -0
- package/ui/boot/Commons/Plural.js +91 -0
- package/ui/boot/Commons/ShellIpc.qml +12 -0
- package/ui/boot/Commons/Style.qml +561 -0
- package/ui/boot/Commons/Translations.qml +100 -0
- package/ui/boot/Commons/UntypedInput.qml +152 -0
- package/ui/boot/Commons/Util.qml +188 -0
- package/ui/boot/Commons/qmldir +13 -0
- package/ui/boot/LICENSE-omarchy +20 -0
- package/ui/boot/Ui/BackgroundMedia.qml +49 -0
- package/ui/boot/Ui/BarIconButton.qml +106 -0
- package/ui/boot/Ui/BarIconContent.qml +38 -0
- package/ui/boot/Ui/BarIndicator.qml +53 -0
- package/ui/boot/Ui/BarWidget.qml +50 -0
- package/ui/boot/Ui/BorderOverlay.qml +54 -0
- package/ui/boot/Ui/BorderSurface.qml +40 -0
- package/ui/boot/Ui/Button.qml +224 -0
- package/ui/boot/Ui/ButtonGroup.qml +131 -0
- package/ui/boot/Ui/ConfirmDialog.qml +134 -0
- package/ui/boot/Ui/CursorSurface.qml +42 -0
- package/ui/boot/Ui/Dropdown.qml +245 -0
- package/ui/boot/Ui/FilterInput.qml +26 -0
- package/ui/boot/Ui/IconContentBounds.qml +141 -0
- package/ui/boot/Ui/KeyboardPanel.qml +453 -0
- package/ui/boot/Ui/MultiSelect.qml +624 -0
- package/ui/boot/Ui/NumberField.qml +86 -0
- package/ui/boot/Ui/OpticalGlyph.qml +68 -0
- package/ui/boot/Ui/OverlayWindow.qml +102 -0
- package/ui/boot/Ui/Panel.qml +60 -0
- package/ui/boot/Ui/PanelActionButton.qml +105 -0
- package/ui/boot/Ui/PanelController.qml +16 -0
- package/ui/boot/Ui/PanelHero.qml +112 -0
- package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
- package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
- package/ui/boot/Ui/PanelSeparator.qml +19 -0
- package/ui/boot/Ui/PanelSlider.qml +156 -0
- package/ui/boot/Ui/PanelToolTip.qml +50 -0
- package/ui/boot/Ui/PluginBarApi.qml +89 -0
- package/ui/boot/Ui/PointerMoveGate.qml +54 -0
- package/ui/boot/Ui/PopupCard.qml +181 -0
- package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
- package/ui/boot/Ui/SearchableDropdown.qml +354 -0
- package/ui/boot/Ui/SizedImage.qml +125 -0
- package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
- package/ui/boot/Ui/TextField.qml +58 -0
- package/ui/boot/Ui/Toggle.qml +118 -0
- package/ui/boot/Ui/ToggleSwitch.qml +115 -0
- package/ui/boot/Ui/WidgetButton.qml +172 -0
- package/ui/boot/Ui/qmldir +41 -0
- package/ui/boot/fleatab.qml +50 -0
- package/ui/boot/picker.qml +39 -0
- package/ui/boot/shell.qml +131 -0
- package/ui/boot/tabtearoff.qml +51 -0
- package/ui/js/Anchor.js +379 -0
- package/ui/js/AnchorHold.js +162 -0
- package/ui/js/Archive.js +157 -0
- package/ui/js/ArchiveCard.js +44 -0
- package/ui/js/ArchiveOps.js +44 -0
- package/ui/js/BackReturn.js +118 -0
- package/ui/js/BulkRename.js +100 -0
- package/ui/js/Buttons.js +54 -0
- package/ui/js/ChromeKeys.js +116 -0
- package/ui/js/ClipMarks.js +73 -0
- package/ui/js/ClipSelection.js +67 -0
- package/ui/js/Clipboard.js +179 -0
- package/ui/js/Cloud.js +97 -0
- package/ui/js/CodeRuns.js +72 -0
- package/ui/js/Collide.js +212 -0
- package/ui/js/ColumnFit.js +28 -0
- package/ui/js/ColumnMenu.js +111 -0
- package/ui/js/Columns.js +298 -0
- package/ui/js/ColumnsCarry.js +103 -0
- package/ui/js/Compare.js +74 -0
- package/ui/js/Contrast.js +157 -0
- package/ui/js/ControlReader.js +42 -0
- package/ui/js/Convert.js +52 -0
- package/ui/js/CopyAs.js +52 -0
- package/ui/js/Crumbs.js +178 -0
- package/ui/js/Csv.js +231 -0
- package/ui/js/Density.js +44 -0
- package/ui/js/Devices.js +440 -0
- package/ui/js/DirSizes.js +180 -0
- package/ui/js/Drag.js +360 -0
- package/ui/js/DragOut.js +166 -0
- package/ui/js/DropDev.js +52 -0
- package/ui/js/Dropbox.js +41 -0
- package/ui/js/Eject.js +217 -0
- package/ui/js/Errors.js +188 -0
- package/ui/js/ExtThumbs.js +115 -0
- package/ui/js/Facts.js +402 -0
- package/ui/js/FigureDate.js +14 -0
- package/ui/js/Filter.js +285 -0
- package/ui/js/Find.js +46 -0
- package/ui/js/Flash.js +184 -0
- package/ui/js/FlashCard.js +79 -0
- package/ui/js/FlashEndings.js +125 -0
- package/ui/js/FlashPane.js +116 -0
- package/ui/js/FleaBin.js +12 -0
- package/ui/js/Focus.js +504 -0
- package/ui/js/FolderSorts.js +108 -0
- package/ui/js/FolderViews.js +44 -0
- package/ui/js/Format.js +314 -0
- package/ui/js/Fuzzy.js +141 -0
- package/ui/js/GlassTag.js +72 -0
- package/ui/js/Grid.js +33 -0
- package/ui/js/GridGeometry.js +90 -0
- package/ui/js/GridNames.js +153 -0
- package/ui/js/GvfsBridge.js +200 -0
- package/ui/js/HostClipboard.js +29 -0
- package/ui/js/Icons.js +218 -0
- package/ui/js/Input.js +50 -0
- package/ui/js/Jump.js +200 -0
- package/ui/js/KeyName.js +64 -0
- package/ui/js/Keymap.js +401 -0
- package/ui/js/Kinds.js +164 -0
- package/ui/js/LICENSES/ISC-lucide.txt +43 -0
- package/ui/js/LangMarkPaths.js +47 -0
- package/ui/js/LangMarks.js +120 -0
- package/ui/js/Lists.js +11 -0
- package/ui/js/LocalSend.js +39 -0
- package/ui/js/LockedMenu.js +63 -0
- package/ui/js/MakeDefault.js +186 -0
- package/ui/js/MarkBox.js +7 -0
- package/ui/js/Markdown.js +119 -0
- package/ui/js/MarkdownLists.js +86 -0
- package/ui/js/MarkdownMaths.js +58 -0
- package/ui/js/MarkdownPictures.js +32 -0
- package/ui/js/MarkdownPrepared.js +139 -0
- package/ui/js/MarkdownTableFit.js +34 -0
- package/ui/js/Marks.js +148 -0
- package/ui/js/Marquee.js +96 -0
- package/ui/js/Match.js +93 -0
- package/ui/js/Material.js +109 -0
- package/ui/js/MdBlocks.js +437 -0
- package/ui/js/MdBreak.js +71 -0
- package/ui/js/MdChunks.js +70 -0
- package/ui/js/MdContainer.js +137 -0
- package/ui/js/MdDocument.js +337 -0
- package/ui/js/MdEmph.js +97 -0
- package/ui/js/MdEntity.js +108 -0
- package/ui/js/MdEntityTable.js +223 -0
- package/ui/js/MdEscape.js +59 -0
- package/ui/js/MdFront.js +37 -0
- package/ui/js/MdHold.js +32 -0
- package/ui/js/MdHtml.js +307 -0
- package/ui/js/MdHtmlBlock.js +290 -0
- package/ui/js/MdHtmlHeading.js +53 -0
- package/ui/js/MdHtmlImage.js +179 -0
- package/ui/js/MdInline.js +154 -0
- package/ui/js/MdItems.js +212 -0
- package/ui/js/MdLeaf.js +309 -0
- package/ui/js/MdLink.js +196 -0
- package/ui/js/MdMath.js +67 -0
- package/ui/js/MdParagraphs.js +138 -0
- package/ui/js/MdRefs.js +232 -0
- package/ui/js/MdResolve.js +226 -0
- package/ui/js/MdRun.js +300 -0
- package/ui/js/MdUrl.js +226 -0
- package/ui/js/Menu.js +514 -0
- package/ui/js/MenuContext.js +85 -0
- package/ui/js/MenuFit.js +13 -0
- package/ui/js/MenuRefresh.js +74 -0
- package/ui/js/MenuWheel.js +35 -0
- package/ui/js/Messages.js +186 -0
- package/ui/js/Motion.js +16 -0
- package/ui/js/Mounts.js +320 -0
- package/ui/js/MouseNav.js +13 -0
- package/ui/js/Nav.js +329 -0
- package/ui/js/NetFs.js +26 -0
- package/ui/js/OpenWith.js +83 -0
- package/ui/js/Ops.js +585 -0
- package/ui/js/Palette.js +33 -0
- package/ui/js/PathBar.js +260 -0
- package/ui/js/Permissions.js +179 -0
- package/ui/js/Phones.js +112 -0
- package/ui/js/Picker.js +353 -0
- package/ui/js/PlaceMenu.js +96 -0
- package/ui/js/Places.js +265 -0
- package/ui/js/PreviewAsk.js +21 -0
- package/ui/js/PreviewKeys.js +155 -0
- package/ui/js/PreviewSettle.js +29 -0
- package/ui/js/PreviewSwap.js +101 -0
- package/ui/js/PropertyFacts.js +144 -0
- package/ui/js/Protocols.js +296 -0
- package/ui/js/QuickLookKinds.js +27 -0
- package/ui/js/QuickLookRect.js +24 -0
- package/ui/js/RailDrop.js +51 -0
- package/ui/js/RailKeys.js +87 -0
- package/ui/js/RailMenu.js +133 -0
- package/ui/js/Recent.js +135 -0
- package/ui/js/RecentMode.js +156 -0
- package/ui/js/Reload.js +60 -0
- package/ui/js/Renderer.js +9 -0
- package/ui/js/RunFile.js +143 -0
- package/ui/js/Scripts.js +28 -0
- package/ui/js/Scroll.js +304 -0
- package/ui/js/ScrollOff.js +69 -0
- package/ui/js/Search.js +478 -0
- package/ui/js/Selection.js +97 -0
- package/ui/js/Settings.js +476 -0
- package/ui/js/SettingsAbout.js +97 -0
- package/ui/js/SettingsShelf.js +52 -0
- package/ui/js/SheetKeys.js +42 -0
- package/ui/js/SheetQuery.js +281 -0
- package/ui/js/ShelfPile.js +27 -0
- package/ui/js/SlowClick.js +53 -0
- package/ui/js/SlowOp.js +6 -0
- package/ui/js/Sort.js +134 -0
- package/ui/js/Startup.js +63 -0
- package/ui/js/StateWords.js +17 -0
- package/ui/js/Status.js +238 -0
- package/ui/js/Swap.js +166 -0
- package/ui/js/TabMove.js +39 -0
- package/ui/js/Tabs.js +751 -0
- package/ui/js/Tags.js +134 -0
- package/ui/js/Taildrop.js +128 -0
- package/ui/js/Tap.js +180 -0
- package/ui/js/TextSize.js +107 -0
- package/ui/js/Thumbs.js +158 -0
- package/ui/js/Transfer.js +172 -0
- package/ui/js/Trash.js +84 -0
- package/ui/js/TrashDates.js +47 -0
- package/ui/js/TrashDrop.js +64 -0
- package/ui/js/UiState.js +438 -0
- package/ui/js/Unlock.js +152 -0
- package/ui/js/Update.js +130 -0
- package/ui/js/UserDirs.js +56 -0
- package/ui/qmldir +230 -0
package/ui/js/Format.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// SI, because a file's size on disk has no power-of-two basis; GLib's own rule and the whole GUI group.
|
|
4
|
+
var BYTES_PER_UNIT = 1000
|
|
5
|
+
var UNITS = ["B", "kB", "MB", "GB", "TB"]
|
|
6
|
+
// GNOME renders a narrow no-break space before the unit, and Flea's neighbours are GLib-formatted.
|
|
7
|
+
var UNIT_SPACE = " "
|
|
8
|
+
|
|
9
|
+
// Below one kilobyte a fraction is noise, so bytes print whole.
|
|
10
|
+
// Counts reach six figures on a real directory, so they are grouped the way the canvas draws them.
|
|
11
|
+
function count(n) {
|
|
12
|
+
var digits = String(n)
|
|
13
|
+
var out = ""
|
|
14
|
+
for (var i = 0; i < digits.length; i++) {
|
|
15
|
+
if (i > 0 && (digits.length - i) % 3 === 0)
|
|
16
|
+
out += ","
|
|
17
|
+
out += digits.charAt(i)
|
|
18
|
+
}
|
|
19
|
+
return out
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function size(bytes) {
|
|
23
|
+
if (bytes < BYTES_PER_UNIT) {
|
|
24
|
+
return bytes + UNIT_SPACE + UNITS[0]
|
|
25
|
+
}
|
|
26
|
+
var value = bytes
|
|
27
|
+
var unit = 0
|
|
28
|
+
while (value >= BYTES_PER_UNIT && unit < UNITS.length - 1) {
|
|
29
|
+
value = value / BYTES_PER_UNIT
|
|
30
|
+
unit += 1
|
|
31
|
+
}
|
|
32
|
+
return value.toFixed(1) + UNIT_SPACE + UNITS[unit]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fills %1 to %9 in one pass: a value prints as given, a dollar sign or placeholder in it included; an unfilled one stays.
|
|
36
|
+
function fill(template) {
|
|
37
|
+
var values = arguments
|
|
38
|
+
return String(template).replace(/%([1-9])/g, function (token, at) { return at < values.length ? String(values[at]) : token })
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function pad(n) {
|
|
42
|
+
return n < 10 ? "0" + n : "" + n
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// "2026-09-12 15:29", the one form every surface prints, in the machine's local wall clock.
|
|
46
|
+
function stamp(d) {
|
|
47
|
+
return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate())
|
|
48
|
+
+ " " + pad(d.getHours()) + ":" + pad(d.getMinutes())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// One form, sortable and unambiguous, so no surface has to invent a relative word for a time.
|
|
52
|
+
function date(mtime) {
|
|
53
|
+
return stamp(new Date(mtime * 1000))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The send picker's column is SendPicker.html's 80 and not the window's 125, which holds about ten
|
|
57
|
+
// characters: the one place the full stamp does not fit. It drops the time and keeps the date, so it
|
|
58
|
+
// is still sortable and still unambiguous. Preview board, "One function, four surfaces".
|
|
59
|
+
function compactDate(mtime) {
|
|
60
|
+
var d = new Date(mtime * 1000)
|
|
61
|
+
return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate())
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The low nine bits of st_mode, read three at a time.
|
|
65
|
+
var PERMISSION_BITS = 9
|
|
66
|
+
var TRIAD = "rwx"
|
|
67
|
+
|
|
68
|
+
function permissions(mode) {
|
|
69
|
+
var out = ""
|
|
70
|
+
for (var i = 0; i < PERMISSION_BITS; i++) {
|
|
71
|
+
var bit = 1 << (PERMISSION_BITS - 1 - i)
|
|
72
|
+
out += (mode & bit) ? TRIAD[i % TRIAD.length] : "-"
|
|
73
|
+
}
|
|
74
|
+
return out
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// The file type lives in the top four bits of st_mode, as S_IFMT masks it.
|
|
78
|
+
var S_IFMT = 0o170000
|
|
79
|
+
var S_IFREG = 0o100000
|
|
80
|
+
var S_IFDIR = 0o040000
|
|
81
|
+
// The owner execute bit, the one Make executable adds.
|
|
82
|
+
var S_IXUSR = 0o100
|
|
83
|
+
var S_IFLNK = 0o120000
|
|
84
|
+
var ANY_EXECUTE_BIT = 0o111
|
|
85
|
+
|
|
86
|
+
function isSymlink(mode) {
|
|
87
|
+
return (mode & S_IFMT) === S_IFLNK
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isExecutable(mode) {
|
|
91
|
+
return (mode & ANY_EXECUTE_BIT) !== 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Shared by Row.qml's iconSource and PreviewMedia.qml's player source: encodeURI leaves # and ?
|
|
95
|
+
// literal, which Qt then reads as a URL fragment or query rather than path bytes.
|
|
96
|
+
function fileUri(path) {
|
|
97
|
+
path = String(path)
|
|
98
|
+
// Only Windows paths use backslashes as separators; POSIX filenames may contain them.
|
|
99
|
+
var drive = /^[A-Za-z]:[\\/]/.test(path)
|
|
100
|
+
var unc = /^\\\\[^\\]/.test(path)
|
|
101
|
+
if (drive || unc) path = path.replace(/\\/g, "/")
|
|
102
|
+
var parts = path.split("/")
|
|
103
|
+
for (var i = 0; i < parts.length; i++) {
|
|
104
|
+
if (!(drive && i === 0)) parts[i] = encodeURIComponent(parts[i])
|
|
105
|
+
}
|
|
106
|
+
return (drive ? "file:///" : path.indexOf("//") === 0 ? "file:" : "file://") + parts.join("/")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// "3:05", or "1:03:05" once an hour is on the clock; mm/ss are always two digits, matching a media player's own clock rather than Format.date's prose.
|
|
110
|
+
function duration(ms) {
|
|
111
|
+
var totalSeconds = Math.max(0, Math.floor(ms / 1000))
|
|
112
|
+
var hours = Math.floor(totalSeconds / 3600)
|
|
113
|
+
var minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
114
|
+
var seconds = totalSeconds % 60
|
|
115
|
+
if (hours > 0) {
|
|
116
|
+
return hours + ":" + pad(minutes) + ":" + pad(seconds)
|
|
117
|
+
}
|
|
118
|
+
return minutes + ":" + pad(seconds)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// The scope reads as the user writes it, so the home prefix comes back as a tilde. Both the search
|
|
122
|
+
// strip and the window chrome draw a path through this, so the rule has one definition.
|
|
123
|
+
// Issue 95, nixfred: a bare prefix made /home/gmx into "~x", a sibling wearing home's name. The test
|
|
124
|
+
// is home itself or home and a separator, the one ui/js/Nav.js crumbs and Search.scopeRoot both make.
|
|
125
|
+
function tilde(path, home) {
|
|
126
|
+
var text = String(path)
|
|
127
|
+
if (home.length > 0 && (text === home || text.indexOf(home + "/") === 0)) {
|
|
128
|
+
return "~" + text.substring(home.length)
|
|
129
|
+
}
|
|
130
|
+
return text
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// A tab is named after the directory it is standing in, so the label is the path's last segment.
|
|
134
|
+
function leafPart(display) {
|
|
135
|
+
var text = String(display)
|
|
136
|
+
var cut = text.lastIndexOf("/")
|
|
137
|
+
if (cut < 0) {
|
|
138
|
+
return text
|
|
139
|
+
}
|
|
140
|
+
// "/" itself has no leaf, and its own separator is the whole label.
|
|
141
|
+
return cut === text.length - 1 ? text : text.substring(cut + 1)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// "44.1 kHz", the way the canvas writes an audio row's Rate; a zero is not a rate and reads empty.
|
|
145
|
+
function sampleRate(hz) {
|
|
146
|
+
var n = Number(hz)
|
|
147
|
+
if (!n || n <= 0) {
|
|
148
|
+
return ""
|
|
149
|
+
}
|
|
150
|
+
var khz = n / 1000
|
|
151
|
+
// A whole number of kilohertz reads without a decimal, so 48000 is "48 kHz" and not "48.0 kHz".
|
|
152
|
+
return (khz === Math.round(khz) ? khz : khz.toFixed(1)) + " kHz"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Issue 67, jesedv: a yanked path is quoted unless a shell reads every character of it as itself.
|
|
156
|
+
function shellQuoted(path) {
|
|
157
|
+
var text = String(path)
|
|
158
|
+
if (/^[A-Za-z0-9_@%+=:,.\/-]+$/.test(text)) {
|
|
159
|
+
return text
|
|
160
|
+
}
|
|
161
|
+
return "'" + text.split("'").join("'\\''") + "'"
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// One window-level day boundary, so no Date is built and no timer ticks per row.
|
|
165
|
+
|
|
166
|
+
// Sample input: Date.now() on the board date, 2026-09-23 11:40 local.
|
|
167
|
+
var DAY_MS = 24 * 60 * 60 * 1000
|
|
168
|
+
// The midnight timer's minute step, so a suspend still lands the day within a minute of resume.
|
|
169
|
+
var MINUTE_MS = 60 * 1000
|
|
170
|
+
|
|
171
|
+
// Local midnight starting the day nowMs falls in, in ms since the epoch.
|
|
172
|
+
function dayStart(nowMs) {
|
|
173
|
+
var now = new Date(nowMs)
|
|
174
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// True when the stamp is on or after today's local midnight.
|
|
178
|
+
function isRecent(mtimeSec, todayStartMs) {
|
|
179
|
+
if (typeof mtimeSec !== "number" || !isFinite(mtimeSec))
|
|
180
|
+
return false
|
|
181
|
+
return mtimeSec * 1000 >= todayStartMs
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Built from components, so a daylight-saving night still lands on midnight.
|
|
185
|
+
function msUntilMidnight(nowMs) {
|
|
186
|
+
var now = new Date(nowMs)
|
|
187
|
+
return new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1).getTime() - nowMs
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Elides in the middle so the extension stays visible; truncation costs a slice, never a relayout.
|
|
191
|
+
// Sample input: "screenshot-2026-08-30-final-review-for-gm-after-the-bench-v3.png", 49.
|
|
192
|
+
|
|
193
|
+
// The walk pairs surrogates by hand, so a cut never splits a code point.
|
|
194
|
+
function charsOf(text) {
|
|
195
|
+
var out = []
|
|
196
|
+
for (var i = 0; i < text.length; i++) {
|
|
197
|
+
var lead = text.charCodeAt(i)
|
|
198
|
+
if (lead >= 0xD800 && lead <= 0xDBFF && i + 1 < text.length) {
|
|
199
|
+
var trail = text.charCodeAt(i + 1)
|
|
200
|
+
if (trail >= 0xDC00 && trail <= 0xDFFF) {
|
|
201
|
+
out.push(text.substring(i, i + 2))
|
|
202
|
+
i += 1
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// A combining mark rides with the char before it, so an NFD accent never orphans.
|
|
207
|
+
if (lead >= 0x0300 && lead <= 0x036F && out.length > 0) {
|
|
208
|
+
out[out.length - 1] += text.charAt(i)
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
out.push(text.charAt(i))
|
|
212
|
+
}
|
|
213
|
+
return out
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Every fast unit sits below U+0300, so a fast name holds no surrogate, no combining mark and no wide glyph.
|
|
217
|
+
var FAST_LIMIT = 0x0300
|
|
218
|
+
|
|
219
|
+
// True when every UTF-16 unit is fast: each char paints one cell and slices never split a point.
|
|
220
|
+
function isFastText(text) {
|
|
221
|
+
for (var i = 0; i < text.length; i++)
|
|
222
|
+
if (text.charCodeAt(i) >= FAST_LIMIT) return false
|
|
223
|
+
return true
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function middleElide(name, maxCells) {
|
|
227
|
+
var text = String(name), max = Math.max(0, Math.floor(maxCells))
|
|
228
|
+
// A non-finite budget names no width, so the name goes through untouched.
|
|
229
|
+
if (!isFinite(max)) return text
|
|
230
|
+
// One scan serves the fit check and the store below, so a fitting fast name builds nothing.
|
|
231
|
+
var fast = isFastText(text)
|
|
232
|
+
// A fast name counts one cell a char, so a short one already fits untouched.
|
|
233
|
+
if (fast && text.length <= max) return text
|
|
234
|
+
var st = fast ? fastStoreOf(text) : storeOf(charsOf(text))
|
|
235
|
+
if (rangeCells(st, 0, st.n) <= max) return text
|
|
236
|
+
// The head takes the odd cell, so a 49-wide board keeps 24 and 24.
|
|
237
|
+
var h = spanFrom(st, 0, Math.ceil((max - 1) / 2)), t = tailSpan(st, Math.floor((max - 1) / 2))
|
|
238
|
+
return pieceOf(st, 0, h) + "…" + pieceOf(st, st.n - t, st.n)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Display cells: East Asian Wide and Fullwidth paint two columns, emoji paint two, all else one.
|
|
242
|
+
// Sample input: cellWidthOf("🎉") is 2, cellWidthOf("A") is 1.
|
|
243
|
+
var WIDE_RANGES = [[0x1100, 0x115F], [0x2E80, 0x303E], [0x3041, 0x33FF], [0x3400, 0x4DBF], [0x4E00, 0xA4CF], [0xAC00, 0xD7A3], [0xF900, 0xFAFF], [0xFE10, 0xFE19], [0xFE30, 0xFE4F], [0xFF00, 0xFF60], [0xFFE0, 0xFFE6], [0x20000, 0x3FFFD]]
|
|
244
|
+
var EMOJI_RANGES = [[0x2600, 0x26FF], [0x2700, 0x27BF], [0x2B00, 0x2BFF], [0x1F000, 0x1FAFF]]
|
|
245
|
+
|
|
246
|
+
// True when a code point paints two columns rather than one.
|
|
247
|
+
function isWideCode(cp) {
|
|
248
|
+
// Every wide range starts at U+1100, so a smaller code point never scans.
|
|
249
|
+
if (cp < 0x1100) return false
|
|
250
|
+
for (var i = 0; i < WIDE_RANGES.length; i++)
|
|
251
|
+
if (cp >= WIDE_RANGES[i][0] && cp <= WIDE_RANGES[i][1]) return true
|
|
252
|
+
for (var j = 0; j < EMOJI_RANGES.length; j++)
|
|
253
|
+
if (cp >= EMOJI_RANGES[j][0] && cp <= EMOJI_RANGES[j][1]) return true
|
|
254
|
+
return false
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// The code point one charsOf element starts with, read without wrapping it in a string.
|
|
258
|
+
// Sample input: codeOf("🎉") is 0x1F389, codeOf("A") is 0x41.
|
|
259
|
+
function codeOf(ch) {
|
|
260
|
+
var s = String(ch), lead = s.charCodeAt(0)
|
|
261
|
+
if (lead >= 0xD800 && lead <= 0xDBFF && s.length > 1) {
|
|
262
|
+
var trail = s.charCodeAt(1)
|
|
263
|
+
if (trail >= 0xDC00 && trail <= 0xDFFF) return 0x10000 + ((lead - 0xD800) << 10) + (trail - 0xDC00)
|
|
264
|
+
}
|
|
265
|
+
return lead
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Cells one char paints: 2 for a wide code point, 1 for all else.
|
|
269
|
+
function cellWidthOf(ch) {
|
|
270
|
+
return isWideCode(codeOf(ch)) ? 2 : 1
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// One layout per call: widths plus prefix sums, so every range below is one subtraction.
|
|
274
|
+
// Sample input: storeOf(["a", "🎉"]) carries widths [1, 2].
|
|
275
|
+
function storeOf(chars) {
|
|
276
|
+
var widths = new Array(chars.length), prefix = new Array(chars.length + 1)
|
|
277
|
+
prefix[0] = 0
|
|
278
|
+
for (var i = 0; i < chars.length; i++) {
|
|
279
|
+
widths[i] = isWideCode(codeOf(chars[i])) ? 2 : 1
|
|
280
|
+
prefix[i + 1] = prefix[i] + widths[i]
|
|
281
|
+
}
|
|
282
|
+
return { seq: chars, widths: widths, prefix: prefix, n: chars.length }
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// The same layout over a fast string: no arrays, every range counts chars as cells.
|
|
286
|
+
function fastStoreOf(text) {
|
|
287
|
+
return { seq: text, widths: null, prefix: null, n: text.length }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Cells of elements [a..b).
|
|
291
|
+
function rangeCells(st, a, b) {
|
|
292
|
+
return st.widths === null ? b - a : st.prefix[b] - st.prefix[a]
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Elements from pos fitting in this many cells.
|
|
296
|
+
function spanFrom(st, pos, budget) {
|
|
297
|
+
if (st.widths === null) return Math.min(Math.max(budget, 0), st.n - pos)
|
|
298
|
+
var used = 0, i = pos
|
|
299
|
+
while (i < st.widths.length && used + st.widths[i] <= budget) { used += st.widths[i]; i += 1 }
|
|
300
|
+
return i - pos
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Trailing elements fitting in this many cells.
|
|
304
|
+
function tailSpan(st, budget) {
|
|
305
|
+
if (st.widths === null) return Math.min(Math.max(budget, 0), st.n)
|
|
306
|
+
var used = 0, i = st.widths.length
|
|
307
|
+
while (i > 0 && used + st.widths[i - 1] <= budget) { used += st.widths[i - 1]; i -= 1 }
|
|
308
|
+
return st.widths.length - i
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Text of elements [a..b): a slice for a fast string, a join for a char array.
|
|
312
|
+
function pieceOf(st, a, b) {
|
|
313
|
+
return st.widths === null ? st.seq.slice(a, b) : st.seq.slice(a, b).join("")
|
|
314
|
+
}
|
package/ui/js/Fuzzy.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// src/backend/fuzzy.rs ported, weight for weight, so the path bar's jump and search agree on what matched
|
|
4
|
+
// and where. Pure; tests/js/jump.js holds it to the Rust tests' own table of exact scores.
|
|
5
|
+
var BONUS_CONSECUTIVE = 8
|
|
6
|
+
var BONUS_BOUNDARY = 6
|
|
7
|
+
var BONUS_BASENAME = 4
|
|
8
|
+
var PENALTY_GAP = 1
|
|
9
|
+
var MAX_STARTS = 16
|
|
10
|
+
|
|
11
|
+
function isSeparator(c) {
|
|
12
|
+
return c === "/" || c === "-" || c === "_" || c === "." || c === " "
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// corner: a character whose lowercase form is longer keeps its first unit, the corner fuzzy.rs documents,
|
|
16
|
+
// and a character outside the basic plane counts as two here where Rust counts one, which moves only a gap charge.
|
|
17
|
+
function fold(text) {
|
|
18
|
+
var out = []
|
|
19
|
+
for (var i = 0; i < text.length; i++) {
|
|
20
|
+
var c = text.charAt(i)
|
|
21
|
+
var lower = c.toLowerCase().charAt(0)
|
|
22
|
+
out.push({ lower: lower, upper: lower !== c })
|
|
23
|
+
}
|
|
24
|
+
return out
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function baseStart(hay) {
|
|
28
|
+
var start = 0
|
|
29
|
+
for (var i = 0; i < hay.length; i++) {
|
|
30
|
+
if (hay[i].lower === "/") {
|
|
31
|
+
start = i + 1
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return start
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function startsAWord(hay, at) {
|
|
38
|
+
if (at === 0) {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
var before = hay[at - 1]
|
|
42
|
+
return isSeparator(before.lower) || (hay[at].upper && !before.upper)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// What one matched character is worth: a run, a boundary and the base name each add, and the
|
|
46
|
+
// characters skipped to reach it are charged back.
|
|
47
|
+
function characterScore(hay, at, base, previous) {
|
|
48
|
+
var score = 0
|
|
49
|
+
if (previous >= 0) {
|
|
50
|
+
score += at === previous + 1 ? BONUS_CONSECUTIVE : -PENALTY_GAP * (at - previous - 1)
|
|
51
|
+
}
|
|
52
|
+
if (startsAWord(hay, at)) {
|
|
53
|
+
score += BONUS_BOUNDARY
|
|
54
|
+
}
|
|
55
|
+
if (at >= base) {
|
|
56
|
+
score += BONUS_BASENAME
|
|
57
|
+
}
|
|
58
|
+
return score
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Greedy from one start: every query character takes the next candidate character that matches it.
|
|
62
|
+
function alignFrom(hay, needle, start, base) {
|
|
63
|
+
var total = 0
|
|
64
|
+
var at = start
|
|
65
|
+
var previous = -1
|
|
66
|
+
var positions = []
|
|
67
|
+
for (var k = 0; k < needle.length; k++) {
|
|
68
|
+
if (k > 0) {
|
|
69
|
+
at++
|
|
70
|
+
while (at < hay.length && hay[at].lower !== needle.charAt(k)) {
|
|
71
|
+
at++
|
|
72
|
+
}
|
|
73
|
+
if (at === hay.length) {
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
total += characterScore(hay, at, base, previous)
|
|
78
|
+
positions.push(at)
|
|
79
|
+
previous = at
|
|
80
|
+
}
|
|
81
|
+
return { score: total, positions: positions }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// null when the query is not a subsequence of the candidate; otherwise the best alignment's score and
|
|
85
|
+
// the positions it matched, which is what the wash is drawn from. The needle is the query lowered once
|
|
86
|
+
// by the caller; the hay is folded once per open by ui/js/Jump.js prepare, never per keystroke.
|
|
87
|
+
function matchFolded(hay, base, needle) {
|
|
88
|
+
if (needle.length === 0) {
|
|
89
|
+
return { score: 0, positions: [] }
|
|
90
|
+
}
|
|
91
|
+
var best = null
|
|
92
|
+
var starts = 0
|
|
93
|
+
for (var i = 0; i < hay.length; i++) {
|
|
94
|
+
if (hay[i].lower !== needle.charAt(0)) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
var found = alignFrom(hay, needle, i, base)
|
|
98
|
+
// A start that cannot finish means no later start can either, the greedy scan's own guarantee.
|
|
99
|
+
if (found === null) {
|
|
100
|
+
return best
|
|
101
|
+
}
|
|
102
|
+
if (best === null || found.score > best.score) {
|
|
103
|
+
best = found
|
|
104
|
+
}
|
|
105
|
+
starts++
|
|
106
|
+
if (starts === MAX_STARTS) {
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return best
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// null when the query is not a subsequence of the candidate; otherwise the best alignment's score and
|
|
114
|
+
// the positions it matched, which is what the wash is drawn from.
|
|
115
|
+
function match(candidate, query) {
|
|
116
|
+
var needle = String(query).toLowerCase()
|
|
117
|
+
if (needle.length === 0) {
|
|
118
|
+
return { score: 0, positions: [] }
|
|
119
|
+
}
|
|
120
|
+
var hay = fold(String(candidate))
|
|
121
|
+
var base = baseStart(hay)
|
|
122
|
+
return matchFolded(hay, base, needle)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The one run the row washes: the longest stretch of consecutive matched positions, the first on a tie.
|
|
126
|
+
function run(positions) {
|
|
127
|
+
var best = { start: -1, length: 0 }
|
|
128
|
+
var i = 0
|
|
129
|
+
while (i < positions.length) {
|
|
130
|
+
var j = i
|
|
131
|
+
while (j + 1 < positions.length && positions[j + 1] === positions[j] + 1) {
|
|
132
|
+
j++
|
|
133
|
+
}
|
|
134
|
+
if (j - i + 1 > best.length) {
|
|
135
|
+
best = { start: positions[i], length: j - i + 1 }
|
|
136
|
+
}
|
|
137
|
+
i = j + 1
|
|
138
|
+
}
|
|
139
|
+
return best
|
|
140
|
+
}
|
|
141
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// The Hyprland tag Omarchy's transparency hotkey sets; Omarchy's own rule then draws the window at its Transparent opacity.
|
|
4
|
+
var TAG = "transparent"
|
|
5
|
+
// Hyprland addresses are lowercase hex with a 0x prefix; nothing else reaches a dispatch.
|
|
6
|
+
var ADDRESS = /^0x[0-9a-f]+$/
|
|
7
|
+
// Prints "address opacity inactive" for each address argument, read from Hyprland's own window properties.
|
|
8
|
+
var PROBE = 'for a in "$@"; do printf "%s %s %s\\n" "$a" "$(hyprctl getprop "address:$a" opacity)" "$(hyprctl getprop "address:$a" opacity_inactive)"; done'
|
|
9
|
+
// Applies "address tag" argument pairs with the Lua dispatcher, else the classic one, as omarchy-hyprland-window-pop does.
|
|
10
|
+
var DISPATCH = 'while [ "$#" -ge 2 ]; do hyprctl dispatch "hl.dsp.window.tag({ window = \\"address:$1\\", tag = \\"$2\\" })" >/dev/null 2>&1 || hyprctl dispatch tagwindow "$2" "address:$1" >/dev/null; shift 2; done'
|
|
11
|
+
|
|
12
|
+
// Sample input: hyprctl clients -j, '[{"address":"0x55bacb0a1f80","pid":4211,"class":"com.thisisgm.flea"}]'.
|
|
13
|
+
function ownAddresses(json, pid) {
|
|
14
|
+
var list
|
|
15
|
+
try {
|
|
16
|
+
list = JSON.parse(json)
|
|
17
|
+
} catch (e) {
|
|
18
|
+
return []
|
|
19
|
+
}
|
|
20
|
+
if (!Array.isArray(list))
|
|
21
|
+
return []
|
|
22
|
+
var out = []
|
|
23
|
+
for (var i = 0; i < list.length; i++) {
|
|
24
|
+
var c = list[i]
|
|
25
|
+
if (c && c.pid === pid && ADDRESS.test(String(c.address)))
|
|
26
|
+
out.push(String(c.address))
|
|
27
|
+
}
|
|
28
|
+
return out
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Sample input, one PROBE line: "0x55bacb0a1f80 0.985 0.96".
|
|
32
|
+
function parseProps(line) {
|
|
33
|
+
var f = String(line).trim().split(/\s+/)
|
|
34
|
+
if (f.length !== 3 || !ADDRESS.test(f[0]))
|
|
35
|
+
return null
|
|
36
|
+
return {address: f[0], opacity: Number(f[1]), inactive: Number(f[2])}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// "add", "remove" or "keep" for one Flea window: the tag goes only where Omarchy draws Transparent and Hyprland leaves the window at 1.
|
|
40
|
+
function decide(mode, usesDefault, opacity, inactive, taggedByFlea) {
|
|
41
|
+
var wanted = mode === "transparent" && usesDefault
|
|
42
|
+
// The hotkey's own test, which also re-tags a window that reuses a closed one's address: a window Hyprland already draws
|
|
43
|
+
// below 1, as stable Omarchy draws every window, keeps its rule.
|
|
44
|
+
if (wanted && opacity === 1 && inactive === 1)
|
|
45
|
+
return "add"
|
|
46
|
+
return taggedByFlea && !wanted ? "remove" : "keep"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// The dispatch pairs for PROBE's output, and the set Flea has tagged once they run; a closed window's record drops out.
|
|
50
|
+
function plan(text, mode, usesDefault, tagged) {
|
|
51
|
+
var next = {}
|
|
52
|
+
var pairs = []
|
|
53
|
+
var lines = String(text).split("\n")
|
|
54
|
+
for (var i = 0; i < lines.length; i++) {
|
|
55
|
+
var w = parseProps(lines[i])
|
|
56
|
+
if (!w)
|
|
57
|
+
continue
|
|
58
|
+
var mine = tagged[w.address] === true
|
|
59
|
+
if (mine)
|
|
60
|
+
next[w.address] = true
|
|
61
|
+
var act = decide(mode, usesDefault, w.opacity, w.inactive, mine)
|
|
62
|
+
if (act === "add") {
|
|
63
|
+
pairs.push(w.address, "+" + TAG)
|
|
64
|
+
next[w.address] = true
|
|
65
|
+
}
|
|
66
|
+
if (act === "remove") {
|
|
67
|
+
pairs.push(w.address, "-" + TAG)
|
|
68
|
+
delete next[w.address]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {pairs: pairs, tagged: next}
|
|
72
|
+
}
|
package/ui/js/Grid.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Filter.js" as Filter
|
|
4
|
+
.import "Marks.js" as Marks
|
|
5
|
+
|
|
6
|
+
// The grid's own geometry: which cell a key means when the rows are tiles rather than lines. Split
|
|
7
|
+
// out of ui/js/Focus.js, which holds the key map and the actions every view shares.
|
|
8
|
+
|
|
9
|
+
// Which way a key steps across a row of tiles, the arrow and the letter the presets spell it with.
|
|
10
|
+
function sideways(event) {
|
|
11
|
+
return event.key === Qt.Key_Left || event.text === "h" ? -1
|
|
12
|
+
: event.key === Qt.Key_Right || event.text === "l" ? 1 : 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Arrow and letter actions share visual cells; absent neighbours clamp even when list navigation wraps.
|
|
16
|
+
function arrow(event, action, root) {
|
|
17
|
+
if (root.viewMode !== "grid") return false
|
|
18
|
+
var columns = root.cursorStride
|
|
19
|
+
var across = sideways(event)
|
|
20
|
+
var delta = (action === "cursorDown" || action === "extendDown") ? columns
|
|
21
|
+
: (action === "cursorUp" || action === "extendUp") ? -columns
|
|
22
|
+
: across < 0 && action === "cursorLeft" ? -1
|
|
23
|
+
: across > 0 && action === "cursorRight" ? 1 : 0
|
|
24
|
+
if (!delta) return false
|
|
25
|
+
var index = Filter.viewOf(root.shown, root.cursorIndex)
|
|
26
|
+
var nextIndex = index + delta
|
|
27
|
+
if (nextIndex < 0 || nextIndex >= root.shownTotal
|
|
28
|
+
|| (across < 0 && index % columns === 0)
|
|
29
|
+
|| (across > 0 && nextIndex % columns === 0)) return true
|
|
30
|
+
if (action === "extendDown" || action === "extendUp") root.extendSelection(delta)
|
|
31
|
+
else { Filter.moveCursor(root, delta); Marks.follow(root) }
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// The grid's tile maths, split out of ui/GridArea.qml so tests/js/gridgeometry.js can pin it.
|
|
4
|
+
|
|
5
|
+
// Width less one gap and the scroll lane, so no tile sits under the bar.
|
|
6
|
+
// Sample input: columnsFor(800, 146, 96, 7, 8) is 5, five tiles across an 800 px grid.
|
|
7
|
+
function columnsFor(width, minCellWidth, tilePixels, rowPaddingX, gap, lane) {
|
|
8
|
+
var cell = Math.max(minCellWidth, tilePixels + 2 * rowPaddingX)
|
|
9
|
+
return Math.max(1, Math.floor((width - gap - (Number(lane) || 0)) / cell))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Sample input: cellWidthFor(800, 5, 8) is 158, five cells filling the 792 beside the gap.
|
|
13
|
+
function cellWidthFor(width, columns, gap, lane) {
|
|
14
|
+
return Math.max(1, Math.floor((width - gap - (Number(lane) || 0)) / Math.max(1, columns)))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// A cell is its slot plus the name gap plus two caption lines plus the pad above and below.
|
|
18
|
+
// Sample input: cellHeightFor(128, 9, 33, 14) is 198, the board tile.
|
|
19
|
+
function cellHeightFor(thumbPixels, gap, captionHeight, padY) {
|
|
20
|
+
return Math.max(1, thumbPixels + gap + captionHeight + 2 * padY)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Tile rows the view keeps built beyond its edge, the one number ui/GridArea.qml's cacheBuffer and a landing's ask both count.
|
|
24
|
+
var cacheRows = 2
|
|
25
|
+
|
|
26
|
+
// The window a grid holds, in whole tile rows: the list's context in pixels (bufferRows of rowHeight each side) rebuilt in tiles, so a screen of tiles keeps the room a screen of rows keeps.
|
|
27
|
+
// Sample input: windowFor(18, 11, 150, 26, 124, 25) is {columns: 18, visible: 11, buffer: 576, size: 1350, margin: 36}, 32 tile rows each side of 11, and a margin of two tile rows.
|
|
28
|
+
function windowFor(columns, visibleTileRows, bufferRows, rowHeight, cellHeight, refetchMargin) {
|
|
29
|
+
var across = Math.max(1, columns)
|
|
30
|
+
var rows = Math.max(1, Math.ceil(bufferRows * rowHeight / Math.max(1, cellHeight)))
|
|
31
|
+
var visible = Math.max(1, visibleTileRows)
|
|
32
|
+
return { columns: across, visible: visible, buffer: across * rows, size: across * (visible + 2 * rows), margin: Math.max(refetchMargin, 2 * across) }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The window the visible items owe the backend once they come within the margin of an edge the held rows have a further side to, or when none are held.
|
|
36
|
+
// Sample input: driftAsk({buffer: 576, size: 1350, margin: 36}, 1000, 1330, 0, 1350, 2000) is {start: 424, count: 1350}, and null at 1000 to 1200.
|
|
37
|
+
function driftAsk(plan, first, last, held, length, total) {
|
|
38
|
+
var end = held + length
|
|
39
|
+
var behind = held > 0 && first - held < plan.margin
|
|
40
|
+
var ahead = end < total && end - last < plan.margin
|
|
41
|
+
return length === 0 || behind || ahead ? { start: Math.max(0, first - plan.buffer), count: plan.size } : null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The window a far cursor landing owes the backend before the view moves, or null for a step or a landing the held rows reach.
|
|
45
|
+
// Sample input: landingAsk(plan, 1999, 0, 2000, 0, 352) with the plan above is {start: 1746, count: 254}, the tail of the listing; with 1500 rows held from 700 it is null.
|
|
46
|
+
function landingAsk(plan, cursorIndex, firstIndex, total, held, heldLength) {
|
|
47
|
+
var row = Math.floor(cursorIndex / plan.columns)
|
|
48
|
+
var seen = Math.floor(firstIndex / plan.columns)
|
|
49
|
+
if (Math.abs(row - seen) <= plan.visible)
|
|
50
|
+
return null
|
|
51
|
+
var before = row > seen ? plan.visible + 1 : cacheRows + 1
|
|
52
|
+
var after = row > seen ? cacheRows + 1 : plan.visible + 1
|
|
53
|
+
var start = Math.max(0, (row - before) * plan.columns - plan.margin)
|
|
54
|
+
var end = Math.min(total, (row + after) * plan.columns + plan.margin)
|
|
55
|
+
if (start >= held && end <= held + heldLength)
|
|
56
|
+
return null
|
|
57
|
+
return { start: start, count: end - start }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Whether two rows draw the same tile: every field equal, so a reply that repeats a row hands its tile nothing to redraw.
|
|
61
|
+
// Sample input: sameRow({n: "a", tg: [2]}, {n: "a", tg: [2]}) is true, sameRow({n: "a"}, null) is false.
|
|
62
|
+
function sameRow(a, b) {
|
|
63
|
+
if (a === b)
|
|
64
|
+
return true
|
|
65
|
+
if (a === null || b === null || a === undefined || b === undefined)
|
|
66
|
+
return false
|
|
67
|
+
var keys = Object.keys(a)
|
|
68
|
+
if (keys.length !== Object.keys(b).length)
|
|
69
|
+
return false
|
|
70
|
+
for (var i = 0; i < keys.length; i++) {
|
|
71
|
+
var x = a[keys[i]], y = b[keys[i]]
|
|
72
|
+
if (x !== y && (typeof x !== "object" || typeof y !== "object" || JSON.stringify(x) !== JSON.stringify(y)))
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The row a tile draws from the held snapshot {start, rows, listing}: the held row, else the row it kept at this index on the same listing, else null; memo is the tile's own {row, index, listing}.
|
|
79
|
+
// Sample input: heldRow({row: null, index: -1, listing: 0}, {start: 10, rows: [a, b], listing: 1}, 11) is b, and a tile at 5 afterwards keeps what it drew at 5.
|
|
80
|
+
function heldRow(memo, held, index) {
|
|
81
|
+
var offset = index - held.start
|
|
82
|
+
var next = offset >= 0 && offset < held.rows.length ? held.rows[offset] : null
|
|
83
|
+
var kept = next === null ? memo.index === index && memo.listing === held.listing : sameRow(memo.row, next)
|
|
84
|
+
memo.index = index
|
|
85
|
+
memo.listing = held.listing
|
|
86
|
+
if (kept)
|
|
87
|
+
return memo.row
|
|
88
|
+
memo.row = next
|
|
89
|
+
return next
|
|
90
|
+
}
|