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,103 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "Columns.js" as Columns
|
|
3
|
+
.import "FolderSorts.js" as FolderSorts
|
|
4
|
+
.import "Format.js" as Format
|
|
5
|
+
|
|
6
|
+
// Navigations carry answered peeks across a listing and refresh them without clearing drawn rows.
|
|
7
|
+
|
|
8
|
+
// A peek answers at most this many rows, src/backend/peek.rs PEEK_CAP.
|
|
9
|
+
var PEEK_CAP = 512
|
|
10
|
+
|
|
11
|
+
// The folder order a column asks under, as the tag its peek key and reply carry.
|
|
12
|
+
// Sample input: orderOf({ folderSorts: {}, rememberSort: true }, "/a", []) is "" for a folder with no order of its own.
|
|
13
|
+
function orderOf(state, path, drawn, newest) {
|
|
14
|
+
var order = FolderSorts.peekOrder(state.folderSorts, path, state.rememberSort, drawn, newest, state.sort)
|
|
15
|
+
return Columns.orderTag(order.by, order.desc)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// A read of the peeked rows that a binding re-evaluates when a peek lands; version is the trigger.
|
|
19
|
+
function rowsFor(peeked, key, path, version) {
|
|
20
|
+
return version >= 0 && path.length > 0 && peeked[key] ? peeked[key] : []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// -1 for a path that was not denied, so mode 0, the denial whose stat failed too, stays its own answer.
|
|
24
|
+
function deniedMode(denials, key, version) {
|
|
25
|
+
return version >= 0 && denials[key] !== undefined ? denials[key] : -1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// What a drag from a peek column's row needs. Sample input: peekSource(pane, "/a", { "/a": { v: 7, w: false } }, 0).
|
|
29
|
+
function peekSource(pane, path, facts, version) {
|
|
30
|
+
var held = version >= 0 ? facts[path] : undefined
|
|
31
|
+
return path.length > 0 ? { pane: pane, dir: path, v: held ? held.v : 0, w: held ? held.w : true } : null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// True when the target is the parent of the shown path: the one hop whose child is the folder left.
|
|
35
|
+
// Sample input: isBack("/a/b/c", "/a/b") is true, isBack("/a/b", "/a/b/c") and isBack("/", "/") are false.
|
|
36
|
+
function isBack(here, target) {
|
|
37
|
+
return String(target) !== String(here) && String(target) === Columns.ancestorParent(here)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// A peek sorts name-first with folders first, so the listing must match that order exactly.
|
|
41
|
+
// Sample input: sameOrder({ by: "name", desc: false, foldersFirst: true, groupByKind: false }, "") is true.
|
|
42
|
+
function sameOrder(list, tag) {
|
|
43
|
+
return list.by === "name" && list.desc !== true && list.foldersFirst !== false && list.groupByKind !== true
|
|
44
|
+
&& (tag === "" || tag === "name0")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// The held window as peek rows; a link or an executable seeds nothing, shaped differently in a peek.
|
|
48
|
+
// Sample input: peekRows([{ n: "a", d: false, i: "text-x-generic", s: 1 }], 1).
|
|
49
|
+
function peekRows(rows, count) {
|
|
50
|
+
var out = []
|
|
51
|
+
for (var i = 0; i < count; i++) {
|
|
52
|
+
var row = rows[i]
|
|
53
|
+
if (!row || row.l !== undefined || (Number(row.p) & Format.S_IFMT) === Format.S_IFLNK || (row.d !== true && row.i === "application-x-executable"))
|
|
54
|
+
return null
|
|
55
|
+
out.push({ n: row.n, d: row.d === true, i: row.i })
|
|
56
|
+
}
|
|
57
|
+
return out
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// A navigation plan, or null before the first listing has anything to carry.
|
|
61
|
+
// Sample input: plan(area at "/a/b/c" listing "/a/b", ["/a"], { foldersFirst: true, groupByKind: false }).pre is ["/a"].
|
|
62
|
+
function plan(area, asks, state) {
|
|
63
|
+
var p = area.pane, here = p.path, target = p.listingPath
|
|
64
|
+
if (!here || !target)
|
|
65
|
+
return null
|
|
66
|
+
var tag = area.orderOf(here), count = Math.min(p.windowSize, PEEK_CAP, p.total)
|
|
67
|
+
var list = { by: p.backend.sortBy, desc: p.backend.sortDesc, foldersFirst: state.foldersFirst, groupByKind: state.groupByKind }
|
|
68
|
+
var clean = p.held === 0 && p.filterQuery === "" && p.searchMode === "" && p.rows.length >= count && sameOrder(list, tag)
|
|
69
|
+
return { target: target, seed: here, tag: tag, pre: asks.filter(function (path) { return path !== target && path !== here }),
|
|
70
|
+
rows: clean ? peekRows(p.rows, count) : null, facts: { v: p.backend.dirDev || 0, w: p.backend.dirWritable !== false } }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// A landing keeps answered drawn folders under the same key and order, plus the old third while its replacement waits.
|
|
74
|
+
function landing(carry, area, asks) {
|
|
75
|
+
var next = { peeked: {}, denials: {}, orders: {}, facts: {}, pending: {} }
|
|
76
|
+
if (!carry || carry.target !== area.pane.path)
|
|
77
|
+
return next
|
|
78
|
+
var paths = asks.slice()
|
|
79
|
+
if (area.shownChildPath && paths.indexOf(area.shownChildPath) < 0) paths.push(area.shownChildPath)
|
|
80
|
+
for (var i = 0; i < paths.length; i++) {
|
|
81
|
+
var path = paths[i], key = area.peekKey(path)
|
|
82
|
+
if (area.peeked[key] === undefined || area.peekOrders[key] !== area.orderOf(path))
|
|
83
|
+
continue
|
|
84
|
+
next.peeked[key] = area.peeked[key]
|
|
85
|
+
next.orders[key] = area.peekOrders[key]
|
|
86
|
+
if (area.denials[key] !== undefined) next.denials[key] = area.denials[key]
|
|
87
|
+
if (area.peekFacts[path] !== undefined) next.facts[path] = area.peekFacts[path]
|
|
88
|
+
}
|
|
89
|
+
var seedKey = area.peekKey(carry.seed), seedTag = area.orderOf(carry.seed)
|
|
90
|
+
if ((asks.indexOf(carry.seed) >= 0 || isBack(carry.seed, carry.target))
|
|
91
|
+
&& carry.rows !== null && carry.tag === seedTag && next.peeked[seedKey] === undefined) {
|
|
92
|
+
next.peeked[seedKey] = carry.rows
|
|
93
|
+
next.orders[seedKey] = seedTag
|
|
94
|
+
next.facts[carry.seed] = carry.facts
|
|
95
|
+
}
|
|
96
|
+
if (asks.indexOf(carry.seed) >= 0 || isBack(carry.seed, carry.target)) paths.push(carry.seed)
|
|
97
|
+
// A watched refresh already out must still land after navigation, even when its folder's settled rows are kept.
|
|
98
|
+
for (var path of paths) {
|
|
99
|
+
var sent = Columns.sentKey(area.peekKey(path), area.pane.windowSize, area.orderOf(path))
|
|
100
|
+
if (Columns.hasAsk(area.pending, sent)) next.pending[sent] = area.pending[sent]
|
|
101
|
+
}
|
|
102
|
+
return next
|
|
103
|
+
}
|
package/ui/js/Compare.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
.import "CopyAs.js" as CopyAs
|
|
5
|
+
.import "Ops.js" as Ops
|
|
6
|
+
|
|
7
|
+
// The Compare overlay's text and rows: ui/CompareView.qml draws what these return and keeps no wording of its own.
|
|
8
|
+
|
|
9
|
+
// The board's sentences for a pair with no patch, by the backend's state (src/backend/compare.rs).
|
|
10
|
+
var SENTENCES = {
|
|
11
|
+
identical: "The two files are identical.",
|
|
12
|
+
binary: "These files are binary, so there is nothing to compare.",
|
|
13
|
+
over: "These files are over 1 MiB, too large to compare.",
|
|
14
|
+
failed: "These files could not be compared."
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var HUNK = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/
|
|
18
|
+
|
|
19
|
+
function sentence(state) {
|
|
20
|
+
return SENTENCES[state] || ""
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Fills %1, %2 ... in one pass, so a file name holding "%2" is never read as a placeholder.
|
|
24
|
+
function fill(template, values) {
|
|
25
|
+
return template.replace(/%(\d)/g, function (whole, n) { return values[Number(n) - 1] })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// "notes-a.md and notes-b.md · 12 lines differ" with a patch; without one, the two names alone.
|
|
29
|
+
// Sample input: title("a.md", "b.md", 1) gives "a.md and b.md · 1 line differs".
|
|
30
|
+
function title(nameA, nameB, differing) {
|
|
31
|
+
if (differing === undefined || differing === null)
|
|
32
|
+
return fill("%1 and %2", [nameA, nameB])
|
|
33
|
+
var count = fill(Ops.pluralWord(differing, "%1 line differs", "%1 lines differ"), [Format.count(differing)])
|
|
34
|
+
return fill("%1 and %2 · %3", [nameA, nameB, count])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// What diff -u prints, as the rows the overlay draws; the --- and +++ lines are the copied patch's, not rows.
|
|
38
|
+
// Sample input: "--- a\n+++ b\n@@ -1,2 +1,2 @@\n x\n-y\n+z\n" gives a hunk row, a context row 1/1, a removed row 2/0 and an added row 0/2.
|
|
39
|
+
function rows(patch) {
|
|
40
|
+
var out = [], oldAt = 0, newAt = 0, inHunk = false
|
|
41
|
+
var lines = String(patch).split("\n")
|
|
42
|
+
if (lines[lines.length - 1] === "")
|
|
43
|
+
lines.pop()
|
|
44
|
+
for (var i = 0; i < lines.length; i++) {
|
|
45
|
+
var line = lines[i], head = HUNK.exec(line)
|
|
46
|
+
if (head) {
|
|
47
|
+
oldAt = Number(head[1])
|
|
48
|
+
newAt = Number(head[3])
|
|
49
|
+
inHunk = true
|
|
50
|
+
out.push({ kind: "hunk", oldNo: 0, newNo: 0, text: line })
|
|
51
|
+
} else if (!inHunk) {
|
|
52
|
+
continue
|
|
53
|
+
} else if (line.charAt(0) === "-") {
|
|
54
|
+
out.push({ kind: "removed", oldNo: oldAt++, newNo: 0, text: line.substring(1) })
|
|
55
|
+
} else if (line.charAt(0) === "+") {
|
|
56
|
+
out.push({ kind: "added", oldNo: 0, newNo: newAt++, text: line.substring(1) })
|
|
57
|
+
} else if (line.charAt(0) === "\\") {
|
|
58
|
+
out.push({ kind: "note", oldNo: 0, newNo: 0, text: line })
|
|
59
|
+
} else {
|
|
60
|
+
out.push({ kind: "context", oldNo: oldAt++, newNo: newAt++, text: line.substring(1) })
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The removed plus added lines, which is what the title counts.
|
|
67
|
+
function changed(rowList) {
|
|
68
|
+
return rowList.filter(function (r) { return r.kind === "removed" || r.kind === "added" }).length
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// The file name a title shows for a path.
|
|
72
|
+
function leaf(path) {
|
|
73
|
+
return CopyAs.leaf(path)
|
|
74
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// WCAG 2.1 relative luminance, so a test can assert a ratio rather than an eye judging a screenshot.
|
|
4
|
+
function channel(c) {
|
|
5
|
+
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function luminance(r, g, b) {
|
|
9
|
+
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Sample input: "#c8ccd0"
|
|
13
|
+
function parse(hex) {
|
|
14
|
+
var s = String(hex).replace("#", "")
|
|
15
|
+
if (s.length === 8)
|
|
16
|
+
s = s.substring(2)
|
|
17
|
+
return [parseInt(s.substr(0, 2), 16) / 255,
|
|
18
|
+
parseInt(s.substr(2, 2), 16) / 255,
|
|
19
|
+
parseInt(s.substr(4, 2), 16) / 255]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function over(fg, alpha, bg) {
|
|
23
|
+
var f = parse(fg)
|
|
24
|
+
var b = parse(bg)
|
|
25
|
+
return [f[0] * alpha + b[0] * (1 - alpha),
|
|
26
|
+
f[1] * alpha + b[1] * (1 - alpha),
|
|
27
|
+
f[2] * alpha + b[2] * (1 - alpha)]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function ratioOf(a, b) {
|
|
31
|
+
var la = luminance(a[0], a[1], a[2])
|
|
32
|
+
var lb = luminance(b[0], b[1], b[2])
|
|
33
|
+
var hi = Math.max(la, lb)
|
|
34
|
+
var lo = Math.min(la, lb)
|
|
35
|
+
return (hi + 0.05) / (lo + 0.05)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ratio(fgHex, bgHex) {
|
|
39
|
+
return ratioOf(parse(fgHex), parse(bgHex))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pad2(n) {
|
|
43
|
+
return n < 16 ? "0" + n.toString(16) : n.toString(16)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hexOf(rgb) {
|
|
47
|
+
return "#" + pad2(Math.round(Math.max(0, Math.min(1, rgb[0])) * 255))
|
|
48
|
+
+ pad2(Math.round(Math.max(0, Math.min(1, rgb[1])) * 255))
|
|
49
|
+
+ pad2(Math.round(Math.max(0, Math.min(1, rgb[2])) * 255))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function mix(a, b, t) {
|
|
53
|
+
return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Keep hue; walk lightness toward black or white until WCAG AA (or minRatio) holds.
|
|
57
|
+
function ensureRatio(fgHex, bgHex, minRatio) {
|
|
58
|
+
var need = minRatio > 0 ? minRatio : 4.5
|
|
59
|
+
if (ratio(fgHex, bgHex) >= need)
|
|
60
|
+
return String(fgHex)
|
|
61
|
+
var fg = parse(fgHex)
|
|
62
|
+
var bg = parse(bgHex)
|
|
63
|
+
var toward = luminance(bg[0], bg[1], bg[2]) > 0.179 ? [0, 0, 0] : [1, 1, 1]
|
|
64
|
+
var lo = 0
|
|
65
|
+
var hi = 1
|
|
66
|
+
var best = toward
|
|
67
|
+
for (var i = 0; i < 18; i++) {
|
|
68
|
+
var mid = (lo + hi) / 2
|
|
69
|
+
var cand = mix(fg, toward, mid)
|
|
70
|
+
if (ratioOf(cand, bg) >= need) {
|
|
71
|
+
best = cand
|
|
72
|
+
hi = mid
|
|
73
|
+
} else {
|
|
74
|
+
lo = mid
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (ratioOf(best, bg) < need)
|
|
78
|
+
best = toward
|
|
79
|
+
return stepped(hexOf(best), bgHex, need, toward)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Rounding to 8 bits can land just under the ratio asked for, 2.99 for 3 on 20 of the 22 stock palettes.
|
|
83
|
+
function stepped(hex, bgHex, need, toward) {
|
|
84
|
+
var c = parse(hex)
|
|
85
|
+
var step = toward[0] > 0.5 ? 1 / 255 : -1 / 255
|
|
86
|
+
var bg = parse(bgHex)
|
|
87
|
+
for (var i = 0; i < 255; i++) {
|
|
88
|
+
if (ratioOf(c, bg) >= need)
|
|
89
|
+
break
|
|
90
|
+
// Each channel clamps on its own, so one already at the extreme cannot carry the others out.
|
|
91
|
+
var next = [clamp(c[0] + step), clamp(c[1] + step), clamp(c[2] + step)]
|
|
92
|
+
// A step that moves no channel at all is the extreme itself, and there is nowhere further to go.
|
|
93
|
+
if (hexOf(next) === hexOf(c))
|
|
94
|
+
break
|
|
95
|
+
c = next
|
|
96
|
+
}
|
|
97
|
+
return hexOf(c)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function clamp(v) {
|
|
101
|
+
return v < 0 ? 0 : (v > 1 ? 1 : v)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The seven tag names, member for member with src/backend/tags.rs NAMES and ui/js/Tags.js NAMES.
|
|
105
|
+
var TAG_NAMES = ["Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Gray"];
|
|
106
|
+
var TAG_ROLES = { Red: "red", Orange: "orange", Yellow: "yellow", Green: "green",
|
|
107
|
+
Blue: "blue", Purple: "magenta", Gray: "dark_foreground" };
|
|
108
|
+
var TAG_HUES = { Red: [335, 12], Orange: [8, 45], Yellow: [28, 70],
|
|
109
|
+
Green: [80, 170], Blue: [195, 255], Purple: [250, 335] };
|
|
110
|
+
|
|
111
|
+
// Each role lifted to 3:1 on the listing ground and the menu ground, plus the digit fallback, decided once per theme load.
|
|
112
|
+
function tagTheme(found, bg, surface) {
|
|
113
|
+
var colors = {};
|
|
114
|
+
var menuColors = {};
|
|
115
|
+
var seen = {};
|
|
116
|
+
var fallback = false;
|
|
117
|
+
for (var i = 0; i < TAG_NAMES.length; i++) {
|
|
118
|
+
var name = TAG_NAMES[i];
|
|
119
|
+
var hex = (found || {})[TAG_ROLES[name]] || "";
|
|
120
|
+
if (!hex) {
|
|
121
|
+
fallback = true;
|
|
122
|
+
} else {
|
|
123
|
+
var key = String(hex).toLowerCase();
|
|
124
|
+
if (seen[key]) fallback = true;
|
|
125
|
+
seen[key] = true;
|
|
126
|
+
}
|
|
127
|
+
colors[name] = hex ? ensureRatio(hex, bg, 3) : "";
|
|
128
|
+
menuColors[name] = hex ? ensureRatio(hex, surface, 3) : "";
|
|
129
|
+
if (name !== "Gray" && hex) {
|
|
130
|
+
var rgb = parse(hex);
|
|
131
|
+
var hi = Math.max(rgb[0], rgb[1], rgb[2]);
|
|
132
|
+
var lo = Math.min(rgb[0], rgb[1], rgb[2]);
|
|
133
|
+
// Almost no chroma is a grey whatever its name claims.
|
|
134
|
+
if (hi === 0 || (hi - lo) / hi < 0.12) fallback = true;
|
|
135
|
+
else if (!tagHueHolds(rgb, TAG_HUES[name])) fallback = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { colors: colors, menuColors: menuColors, fallback: fallback };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// A hue inside a window that wraps past red, so one comparison covers both ends.
|
|
142
|
+
function tagHueHolds(rgb, window) {
|
|
143
|
+
var hi = Math.max(rgb[0], rgb[1], rgb[2]);
|
|
144
|
+
var lo = Math.min(rgb[0], rgb[1], rgb[2]);
|
|
145
|
+
var delta = hi - lo;
|
|
146
|
+
var hue = 0;
|
|
147
|
+
if (delta > 0) {
|
|
148
|
+
if (hi === rgb[0]) {
|
|
149
|
+
hue = 60 * (((rgb[1] - rgb[2]) / delta + 6) % 6);
|
|
150
|
+
} else if (hi === rgb[1]) {
|
|
151
|
+
hue = 60 * ((rgb[2] - rgb[0]) / delta + 2);
|
|
152
|
+
} else {
|
|
153
|
+
hue = 60 * ((rgb[0] - rgb[1]) / delta + 4);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return window[0] <= window[1] ? hue >= window[0] && hue <= window[1] : hue >= window[0] || hue <= window[1];
|
|
157
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// Sample input: read(window, window.contentItem, /^(DialogButton)_QML/, {}) === {focus: {kind: "DialogButton", via: "owner", ...}, controls: [{kind: "DialogButton", name: "Open", x: 900, ...}]}.
|
|
4
|
+
// The cohesion captures' reader, shared by the window and the picker: every visible control that draws focus, its window box and its states.
|
|
5
|
+
function read(quick, top, kinds, alias) {
|
|
6
|
+
var out = []
|
|
7
|
+
function kindOf(item) { var m = String(item).match(kinds); return m ? (alias[m[1]] || m[1]) : "" }
|
|
8
|
+
function pointerFlag(item, name) {
|
|
9
|
+
if (item[name] !== undefined) return item[name]
|
|
10
|
+
var handler = name === "hovered" ? "QQuickHoverHandler" : "QQuickTapHandler"
|
|
11
|
+
var data = item.data || []
|
|
12
|
+
for (var i = 0; i < data.length; i++)
|
|
13
|
+
if (data[i] && String(data[i]).indexOf(handler) === 0) return data[i][name]
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
function entry(item, kind, via) {
|
|
17
|
+
var p = item.mapToItem(null, 0, 0)
|
|
18
|
+
var hovered = pointerFlag(item, "hovered"), pressed = pointerFlag(item, "pressed")
|
|
19
|
+
return {kind: kind, name: String(item.label || item.accessName || item.title || item.objectName || item.name || item.glyph || ""),
|
|
20
|
+
x: Math.round(p.x), y: Math.round(p.y), w: Math.round(item.width), h: Math.round(item.height),
|
|
21
|
+
focused: !!(item.activeFocus || item.focused === true || item.keyboardFocused === true),
|
|
22
|
+
hovered: hovered === true, pressed: pressed === true,
|
|
23
|
+
hoverable: hovered !== undefined, pressable: pressed !== undefined,
|
|
24
|
+
enabled: !!item.enabled && item.available !== false && item.inputLive !== false,
|
|
25
|
+
value: item.value !== undefined ? String(item.value) : "",
|
|
26
|
+
primary: item.primary === true, destructive: item.destructive === true, member: item.setMember === true, via: via}
|
|
27
|
+
}
|
|
28
|
+
function walk(item) {
|
|
29
|
+
if (!item || !item.visible || item.opacity <= 0) return
|
|
30
|
+
var kind = kindOf(item)
|
|
31
|
+
if (kind) out.push(entry(item, kind, ""))
|
|
32
|
+
for (var i = 0; i < item.children.length; i++) walk(item.children[i])
|
|
33
|
+
}
|
|
34
|
+
walk(top)
|
|
35
|
+
var item = quick ? quick.activeFocusItem : null, owner = item
|
|
36
|
+
while (owner && !kindOf(owner)) owner = owner.parent
|
|
37
|
+
// A dialog that keeps the keyboard on its own scope names its focused control through that control's focused flag.
|
|
38
|
+
var focus = owner ? entry(owner, kindOf(owner), "owner") : null
|
|
39
|
+
for (var j = 0; !focus && j < out.length; j++) if (out[j].focused) focus = Object.assign({}, out[j], {via: "flag"})
|
|
40
|
+
if (!focus && item) focus = entry(item, String(item).split("(")[0], "item")
|
|
41
|
+
return JSON.stringify({focus: focus, controls: out})
|
|
42
|
+
}
|
package/ui/js/Convert.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// The convert popup's own two decisions: which format, and whether to strip. The destination name is
|
|
4
|
+
// built here because the popup is what decides it; the wire carries no format field at all, since
|
|
5
|
+
// ImageMagick reads the codec off the destination's extension.
|
|
6
|
+
|
|
7
|
+
// What the popup offers. Every one of these is in this box's ImageMagick delegate list.
|
|
8
|
+
var FORMATS = ["jpg", "png", "webp", "avif", "heic", "tiff", "bmp"]
|
|
9
|
+
|
|
10
|
+
// The canvas's own suffixes: a real conversion is "(converted)", a same-format strip is "(stripped)".
|
|
11
|
+
var CONVERTED = "(converted)"
|
|
12
|
+
var STRIPPED = "(stripped)"
|
|
13
|
+
|
|
14
|
+
function extensionOf(name) {
|
|
15
|
+
var cut = String(name).lastIndexOf(".")
|
|
16
|
+
return cut > 0 ? String(name).substring(cut + 1).toLowerCase() : ""
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function stemOf(name) {
|
|
20
|
+
var cut = String(name).lastIndexOf(".")
|
|
21
|
+
return cut > 0 ? String(name).substring(0, cut) : String(name)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Never the file it came from: an interrupted in-place strip destroys the file it was given, so no
|
|
25
|
+
// in-place option is offered at all.
|
|
26
|
+
function destName(name, format) {
|
|
27
|
+
var want = String(format).replace(/^\./, "").toLowerCase()
|
|
28
|
+
var word = want === extensionOf(name) ? STRIPPED : CONVERTED
|
|
29
|
+
return stemOf(name) + " " + word + "." + want
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The format row that starts selected: the canvas shows JPEG picked on a PNG, so it is the first
|
|
33
|
+
// format that is not the one the file already is.
|
|
34
|
+
function defaultFormat(name) {
|
|
35
|
+
var current = extensionOf(name)
|
|
36
|
+
for (var i = 0; i < FORMATS.length; i++) {
|
|
37
|
+
if (FORMATS[i] !== current) {
|
|
38
|
+
return FORMATS[i]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return FORMATS[0]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function destination(source, format) {
|
|
45
|
+
var parent = source.path.substring(0, source.path.lastIndexOf("/") + 1)
|
|
46
|
+
return parent + destName(source.name, format)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function matchesReply(source, requestId, reply) {
|
|
50
|
+
return source !== null && reply.requestId === requestId
|
|
51
|
+
&& reply.source === source.path
|
|
52
|
+
}
|
package/ui/js/CopyAs.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
.import "Format.js" as Format
|
|
4
|
+
|
|
5
|
+
// Sample input: "/home/gm/archive.tar.gz"; every Copy as variant covers the
|
|
6
|
+
// whole selection, one path per line, as plain text.
|
|
7
|
+
function leaf(path) {
|
|
8
|
+
var text = String(path)
|
|
9
|
+
var cut = text.lastIndexOf("/")
|
|
10
|
+
return cut < 0 ? text : text.substring(cut + 1)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Sample input: "/home/gm/archive.tar.gz" -> "archive.tar"; a dotfile keeps
|
|
14
|
+
// its whole name, so ".bashrc" never becomes an empty stem.
|
|
15
|
+
function stem(path) {
|
|
16
|
+
var name = leaf(path)
|
|
17
|
+
var start = name.charAt(0) === "." ? 1 : 0
|
|
18
|
+
var dot = name.lastIndexOf(".")
|
|
19
|
+
if (dot <= start)
|
|
20
|
+
return name
|
|
21
|
+
return name.substring(0, dot)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Sample input: "/home/gm/a.txt" -> "/home/gm"; "/" stays "/".
|
|
25
|
+
function dirpath(path) {
|
|
26
|
+
var text = String(path)
|
|
27
|
+
var cut = text.lastIndexOf("/")
|
|
28
|
+
if (cut < 0)
|
|
29
|
+
return text
|
|
30
|
+
return cut === 0 ? "/" : text.substring(0, cut)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function one(path, kind) {
|
|
34
|
+
if (kind === "name")
|
|
35
|
+
return leaf(path)
|
|
36
|
+
if (kind === "stem")
|
|
37
|
+
return stem(path)
|
|
38
|
+
if (kind === "dirpath")
|
|
39
|
+
return dirpath(path)
|
|
40
|
+
if (kind === "uri")
|
|
41
|
+
return Format.fileUri(String(path))
|
|
42
|
+
if (kind === "quoted")
|
|
43
|
+
return "'" + String(path).split("'").join("'\\''") + "'"
|
|
44
|
+
return String(path)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function lines(paths, kind) {
|
|
48
|
+
var out = []
|
|
49
|
+
for (var i = 0; i < paths.length; i++)
|
|
50
|
+
out.push(one(paths[i], kind))
|
|
51
|
+
return out.join("\n")
|
|
52
|
+
}
|
package/ui/js/Crumbs.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// The chrome's breadcrumb: one path as the pieces a click can land on, and what fits when the bar is
|
|
4
|
+
// narrower than the path. Split out of ui/js/Nav.js, which walks the tree; this only draws where it is.
|
|
5
|
+
|
|
6
|
+
// Issue 45: the chrome's path as the pieces a click can land on. text is what is drawn, including
|
|
7
|
+
// the separator that follows it, so the pieces concatenate to exactly the one line they replace;
|
|
8
|
+
// path is the directory the piece names, which is what ui/ChromeBar.qml hands to pathEntered. The
|
|
9
|
+
// home test is the whole-component one, home itself or home and a separator, which ui/js/Format.js
|
|
10
|
+
// tilde and ui/js/Search.js scopeRoot now both make too: a sibling like /home/gmx is not inside home.
|
|
11
|
+
function crumbs(path, home, rootAlias) {
|
|
12
|
+
var text = String(path)
|
|
13
|
+
if (text === "trash:///") return [{ text: text, path: text, last: true }]
|
|
14
|
+
var aliasRoot = rootAlias ? cleanRoot(rootAlias.path) : ""
|
|
15
|
+
var aliasLabel = rootAlias ? String(rootAlias.label || "") : ""
|
|
16
|
+
var aliased = aliasRoot.length > 0 && aliasLabel.length > 0 && inside(text, aliasRoot)
|
|
17
|
+
if (aliased) {
|
|
18
|
+
// The label is one crumb even when it holds a "/"; only the real path's suffix adds separators and targets.
|
|
19
|
+
var aliasedParts = text.substring(aliasRoot.length).split("/").filter(function (part) { return part.length > 0 })
|
|
20
|
+
var aliasedOut = [{ text: aliasLabel + (aliasedParts.length > 0 ? "/" : ""),
|
|
21
|
+
path: aliasRoot, last: false }]
|
|
22
|
+
var aliasedWalked = aliasRoot
|
|
23
|
+
for (var a = 0; a < aliasedParts.length; a++) {
|
|
24
|
+
aliasedWalked += "/" + aliasedParts[a]
|
|
25
|
+
aliasedOut.push({ text: aliasedParts[a] + "/", path: aliasedWalked, last: false })
|
|
26
|
+
}
|
|
27
|
+
var aliasedEnd = aliasedOut[aliasedOut.length - 1]
|
|
28
|
+
if (aliasedOut.length > 1)
|
|
29
|
+
aliasedEnd.text = aliasedEnd.text.substring(0, aliasedEnd.text.length - 1)
|
|
30
|
+
aliasedEnd.last = true
|
|
31
|
+
return aliasedOut
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var base = String(home)
|
|
35
|
+
var inHome = base.length > 0 && inside(text, base)
|
|
36
|
+
var display = inHome ? "~" + text.substring(base.length) : text
|
|
37
|
+
var parts = display.split("/")
|
|
38
|
+
var walked = inHome ? base : ""
|
|
39
|
+
// The leading "~" and the leading "/" are each a crumb of their own: one names home and the
|
|
40
|
+
// other names the root, and neither is a component the split hands back.
|
|
41
|
+
var out = [{ text: parts.length > 1 ? parts[0] + "/" : parts[0],
|
|
42
|
+
path: walked.length > 0 ? walked : "/", last: false }]
|
|
43
|
+
for (var i = 1; i < parts.length; i++) {
|
|
44
|
+
if (parts[i].length === 0) {
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
walked = walked + "/" + parts[i]
|
|
48
|
+
out.push({ text: parts[i] + "/", path: walked, last: false })
|
|
49
|
+
}
|
|
50
|
+
// Only a crumb with another after it carries a separator, so the last one gives its own back.
|
|
51
|
+
var end = out[out.length - 1]
|
|
52
|
+
if (out.length > 1) {
|
|
53
|
+
end.text = end.text.substring(0, end.text.length - 1)
|
|
54
|
+
}
|
|
55
|
+
end.last = true
|
|
56
|
+
return out
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function cleanRoot(path) {
|
|
60
|
+
var text = String(path || "")
|
|
61
|
+
while (text.length > 1 && text.charAt(text.length - 1) === "/")
|
|
62
|
+
text = text.substring(0, text.length - 1)
|
|
63
|
+
return text
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function inside(path, root) {
|
|
67
|
+
return path === root || path.indexOf(root + "/") === 0
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Issue 237: the deepest remembered display root wins, so nested WebDAV spaces on one host stay distinct.
|
|
71
|
+
function aliasAt(path, aliases) {
|
|
72
|
+
var text = String(path || "")
|
|
73
|
+
var best = null
|
|
74
|
+
for (var i = 0; aliases && i < aliases.length; i++) {
|
|
75
|
+
var root = cleanRoot(aliases[i].path)
|
|
76
|
+
if (root.length > 0 && inside(text, root) && (!best || root.length > best.path.length))
|
|
77
|
+
best = { path: root, label: String(aliases[i].label || ""), key: aliases[i].key }
|
|
78
|
+
}
|
|
79
|
+
return best && best.label.length > 0 ? best : null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function rememberAlias(aliases, path, label, key) {
|
|
83
|
+
var root = cleanRoot(path)
|
|
84
|
+
var name = String(label || "")
|
|
85
|
+
if (root.length === 0 || name.length === 0)
|
|
86
|
+
return aliases || []
|
|
87
|
+
var id = String(key || root)
|
|
88
|
+
var out = []
|
|
89
|
+
for (var i = 0; aliases && i < aliases.length; i++) {
|
|
90
|
+
if (aliases[i].key !== id && cleanRoot(aliases[i].path) !== root)
|
|
91
|
+
out.push(aliases[i])
|
|
92
|
+
}
|
|
93
|
+
out.push({ path: root, label: name, key: id })
|
|
94
|
+
return out
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Only a persisted source may rename an alias; a live mount's generated label is not a saved name.
|
|
98
|
+
function labelsByKey(entries, keyFor) {
|
|
99
|
+
var out = {}
|
|
100
|
+
for (var i = 0; entries && i < entries.length; i++) {
|
|
101
|
+
var address = entries[i].uri || entries[i].path
|
|
102
|
+
if (entries[i].saved === true && address && entries[i].label)
|
|
103
|
+
out[keyFor(address)] = entries[i].label
|
|
104
|
+
}
|
|
105
|
+
return out
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// A saved-place rename changes the visible root without changing a real path or navigation target.
|
|
109
|
+
function relabelAliases(aliases, labels) {
|
|
110
|
+
var changed = false
|
|
111
|
+
var out = []
|
|
112
|
+
for (var i = 0; aliases && i < aliases.length; i++) {
|
|
113
|
+
var name = labels && labels[aliases[i].key] ? String(labels[aliases[i].key]) : aliases[i].label
|
|
114
|
+
changed = changed || name !== aliases[i].label
|
|
115
|
+
out.push(name === aliases[i].label ? aliases[i]
|
|
116
|
+
: { path: aliases[i].path, label: name, key: aliases[i].key })
|
|
117
|
+
}
|
|
118
|
+
return changed ? out : (aliases || [])
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Below this a leaf gives up more to the ellipsis than the ellipsis saves, so it is drawn whole.
|
|
122
|
+
var LEAF_FLOOR = 6
|
|
123
|
+
|
|
124
|
+
// What the crumbs before it leave the leaf, in characters.
|
|
125
|
+
function leafRoom(list, budget) {
|
|
126
|
+
return budget - (crumbChars(list) - list[list.length - 1].text.length)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Chrome rule 2 collapses whole crumbs, but the leaf is the one crumb that cannot be dropped: when
|
|
130
|
+
// even it does not fit, it takes the ellipsis in its own middle rather than a cut at the strip's edge.
|
|
131
|
+
function elideLeaf(list, budget) {
|
|
132
|
+
var end = list[list.length - 1]
|
|
133
|
+
var room = leafRoom(list, budget)
|
|
134
|
+
if (room >= end.text.length || end.text.length <= LEAF_FLOOR) {
|
|
135
|
+
return list
|
|
136
|
+
}
|
|
137
|
+
// corner: a strip with less room than the floor draws the floor, which is short rather than cut.
|
|
138
|
+
var keep = Math.max(LEAF_FLOOR, room)
|
|
139
|
+
var head = Math.ceil((keep - 1) / 2)
|
|
140
|
+
var tail = keep - 1 - head
|
|
141
|
+
var cut = end.text.substring(0, head) + "\u2026" + (tail > 0 ? end.text.substring(end.text.length - tail) : "")
|
|
142
|
+
return list.slice(0, list.length - 1).concat([{ text: cut, path: end.path, last: true }])
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Chrome rule 2: a path too long for the strip reads as its root, one collapsed crumb and the segments nearest you, whole crumbs only, so the marker is a crumb of its own. budget is the strip's width in characters, monospace.
|
|
146
|
+
function fitCrumbs(list, budget) {
|
|
147
|
+
if (list.length < 4 || crumbChars(list) <= budget) {
|
|
148
|
+
return elideLeaf(list, budget)
|
|
149
|
+
}
|
|
150
|
+
var shown = [list[0], { text: "\u2026/", path: "", last: false, elided: true },
|
|
151
|
+
list[list.length - 2], list[list.length - 1]]
|
|
152
|
+
// Whatever room the marker leaves goes back to the crumbs nearest the root, in walking order.
|
|
153
|
+
for (var i = 1; i < list.length - 2; i++) {
|
|
154
|
+
var next = shown.slice()
|
|
155
|
+
next.splice(i, 0, list[i])
|
|
156
|
+
if (crumbChars(next) > budget) {
|
|
157
|
+
break
|
|
158
|
+
}
|
|
159
|
+
shown = next
|
|
160
|
+
}
|
|
161
|
+
// A parent that leaves the leaf less than it needs and less than its floor goes the way the
|
|
162
|
+
// middle went: rule 2 elides whole crumbs, and the leaf is the one that may not be the one to go.
|
|
163
|
+
if (shown.length === 4) {
|
|
164
|
+
var left = leafRoom(shown, budget)
|
|
165
|
+
if (left < shown[3].text.length && left < LEAF_FLOOR) {
|
|
166
|
+
shown = [shown[0], shown[1], shown[3]]
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return elideLeaf(shown, budget)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function crumbChars(list) {
|
|
173
|
+
var chars = 0
|
|
174
|
+
for (var i = 0; i < list.length; i++) {
|
|
175
|
+
chars += list[i].text.length
|
|
176
|
+
}
|
|
177
|
+
return chars
|
|
178
|
+
}
|