@esportsplus/ui 0.56.4 → 0.57.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/NOTICE +232 -0
- package/README.md +129 -138
- package/build/components/accordion/index.d.ts +23 -9
- package/build/components/accordion/index.js +95 -11
- package/build/components/accordion/scss/index.scss +1 -1
- package/build/components/announcement/index.d.ts +2167 -0
- package/build/components/announcement/index.js +35 -0
- package/build/components/announcement/scss/index.scss +2 -0
- package/build/components/banner/scss/index.scss +1 -1
- package/build/components/breadcrumb/index.d.ts +25 -25
- package/build/components/breadcrumb/index.js +189 -17
- package/build/components/breadcrumb/scss/index.scss +1 -1
- package/build/components/button/copy.js +6 -6
- package/build/components/button/faces.js +4 -4
- package/build/components/button/fan.js +85 -84
- package/build/components/button/hold.js +8 -10
- package/build/components/button/icons.js +33 -10
- package/build/components/button/index.d.ts +6 -0
- package/build/components/button/index.js +4 -1
- package/build/components/button/kbd.js +25 -25
- package/build/components/button/loading.js +7 -7
- package/build/components/button/morph.d.ts +22 -0
- package/build/components/button/morph.js +115 -0
- package/build/components/button/scss/index.scss +1 -1
- package/build/components/button/sidebar.d.ts +18 -0
- package/build/components/button/sidebar.js +19 -0
- package/build/components/button/split.d.ts +23 -0
- package/build/components/button/split.js +41 -0
- package/build/components/capslock/index.d.ts +19 -0
- package/build/components/capslock/index.js +39 -0
- package/build/components/capslock/scss/index.scss +2 -0
- package/build/components/card/expand.d.ts +22 -0
- package/build/components/card/expand.js +118 -0
- package/build/components/card/index.d.ts +6 -0
- package/build/components/card/index.js +4 -0
- package/build/components/card/scss/index.scss +1 -1
- package/build/components/cc/index.d.ts +30 -0
- package/build/components/cc/index.js +569 -0
- package/build/components/cc/scss/index.scss +2 -0
- package/build/components/chat-minimap/index.d.ts +28 -0
- package/build/components/chat-minimap/index.js +223 -0
- package/build/components/chat-minimap/range.d.ts +9 -0
- package/build/components/chat-minimap/range.js +30 -0
- package/build/components/chat-minimap/scss/index.scss +2 -0
- package/build/components/chat-minimap/spy.d.ts +14 -0
- package/build/components/chat-minimap/spy.js +107 -0
- package/build/components/checkbox/group.d.ts +33 -0
- package/build/components/checkbox/group.js +156 -0
- package/build/components/checkbox/index.d.ts +3 -1456
- package/build/components/checkbox/index.js +3 -69
- package/build/components/checkbox/options.d.ts +6513 -0
- package/build/components/checkbox/options.js +79 -0
- package/build/components/checkbox/scss/index.scss +1 -1
- package/build/components/clipboard/copy.js +5 -5
- package/build/components/clipboard/index.d.ts +15 -3
- package/build/components/color-picker/index.d.ts +5 -7
- package/build/components/color-picker/index.js +175 -222
- package/build/components/color-picker/scss/index.scss +1 -1
- package/build/components/command/index.d.ts +83 -17
- package/build/components/command/index.js +556 -143
- package/build/components/command/scss/index.scss +1 -1
- package/build/components/container/scss/index.scss +1 -1
- package/build/components/copy/index.d.ts +23 -0
- package/build/components/copy/index.js +61 -0
- package/build/components/copy/scss/index.scss +2 -0
- package/build/components/counter/index.d.ts +3 -2
- package/build/components/counter/index.js +41 -73
- package/build/components/counter/scss/index.scss +1 -1
- package/build/components/datalist/index.d.ts +5 -0
- package/build/components/datalist/index.js +76 -49
- package/build/components/datalist/scss/index.scss +1 -1
- package/build/components/dock/index.d.ts +5 -4
- package/build/components/dock/index.js +62 -171
- package/build/components/dock/scss/index.scss +1 -1
- package/build/components/editor/code/changes.d.ts +15 -0
- package/build/components/editor/code/changes.js +60 -0
- package/build/components/editor/code/clipboard.d.ts +6 -0
- package/build/components/editor/code/clipboard.js +50 -0
- package/build/components/editor/code/colors.d.ts +11 -0
- package/build/components/editor/code/colors.js +172 -0
- package/build/components/editor/code/commands.d.ts +22 -0
- package/build/components/editor/code/commands.js +556 -0
- package/build/components/editor/code/composition.d.ts +23 -0
- package/build/components/editor/code/composition.js +67 -0
- package/build/components/editor/code/conflicts.d.ts +21 -0
- package/build/components/editor/code/conflicts.js +91 -0
- package/build/components/editor/code/document.d.ts +127 -0
- package/build/components/editor/code/document.js +477 -0
- package/build/components/editor/code/editor.d.ts +21 -0
- package/build/components/editor/code/editor.js +30 -0
- package/build/components/editor/code/features.d.ts +77 -0
- package/build/components/editor/code/features.js +833 -0
- package/build/components/editor/code/field.d.ts +14 -0
- package/build/components/editor/code/field.js +57 -0
- package/build/components/editor/code/find.d.ts +83 -0
- package/build/components/editor/code/find.js +312 -0
- package/build/components/editor/code/folding.d.ts +34 -0
- package/build/components/editor/code/folding.js +433 -0
- package/build/components/editor/code/goto.d.ts +22 -0
- package/build/components/editor/code/goto.js +91 -0
- package/build/components/editor/code/gutter.d.ts +5 -0
- package/build/components/editor/code/gutter.js +22 -0
- package/build/components/editor/code/index.d.ts +17 -0
- package/build/components/editor/code/index.js +7 -0
- package/build/components/editor/code/keymap.d.ts +30 -0
- package/build/components/editor/code/keymap.js +356 -0
- package/build/components/editor/code/layout.d.ts +67 -0
- package/build/components/editor/code/layout.js +350 -0
- package/build/components/editor/code/links.d.ts +8 -0
- package/build/components/editor/code/links.js +32 -0
- package/build/components/editor/code/minimap.d.ts +46 -0
- package/build/components/editor/code/minimap.js +172 -0
- package/build/components/editor/code/navigation.d.ts +17 -0
- package/build/components/editor/code/navigation.js +49 -0
- package/build/components/editor/code/pointer.d.ts +40 -0
- package/build/components/editor/code/pointer.js +134 -0
- package/build/components/editor/code/projection.d.ts +43 -0
- package/build/components/editor/code/projection.js +200 -0
- package/build/components/editor/code/rows.d.ts +50 -0
- package/build/components/editor/code/rows.js +296 -0
- package/build/components/editor/code/search.d.ts +24 -0
- package/build/components/editor/code/search.js +231 -0
- package/build/components/editor/code/selection.d.ts +2 -0
- package/build/components/editor/code/selection.js +53 -0
- package/build/components/editor/code/services/completion.d.ts +27 -0
- package/build/components/editor/code/services/completion.js +90 -0
- package/build/components/editor/code/services/diagnostics.d.ts +28 -0
- package/build/components/editor/code/services/diagnostics.js +98 -0
- package/build/components/editor/code/services/hover.d.ts +14 -0
- package/build/components/editor/code/services/hover.js +66 -0
- package/build/components/editor/code/services/index.d.ts +27 -0
- package/build/components/editor/code/services/index.js +64 -0
- package/build/components/editor/code/services/model.d.ts +35 -0
- package/build/components/editor/code/services/model.js +167 -0
- package/build/components/editor/code/services/protocol.d.ts +68 -0
- package/build/components/editor/code/services/protocol.js +1 -0
- package/build/components/editor/code/services/session.d.ts +54 -0
- package/build/components/editor/code/services/session.js +496 -0
- package/build/components/editor/code/services/transport.d.ts +12 -0
- package/build/components/editor/code/services/transport.js +18 -0
- package/build/components/editor/code/syntax.d.ts +83 -0
- package/build/components/editor/code/syntax.js +1106 -0
- package/build/components/editor/code/unicode.d.ts +10 -0
- package/build/components/editor/code/unicode.js +117 -0
- package/build/components/editor/code/view.d.ts +122 -0
- package/build/components/editor/code/view.js +1560 -0
- package/build/components/editor/diffs/diff3.d.ts +25 -0
- package/build/components/editor/diffs/diff3.js +152 -0
- package/build/components/editor/diffs/engine.d.ts +18 -0
- package/build/components/editor/diffs/engine.js +110 -0
- package/build/components/editor/diffs/hunks.d.ts +41 -0
- package/build/components/editor/diffs/hunks.js +96 -0
- package/build/components/editor/diffs/index.d.ts +18 -0
- package/build/components/editor/diffs/index.js +10 -0
- package/build/components/editor/diffs/merge.d.ts +23 -0
- package/build/components/editor/diffs/merge.js +324 -0
- package/build/components/editor/diffs/render.d.ts +6 -0
- package/build/components/editor/diffs/render.js +56 -0
- package/build/components/editor/diffs/view.d.ts +44 -0
- package/build/components/editor/diffs/view.js +363 -0
- package/build/components/editor/diffs/words.d.ts +8 -0
- package/build/components/editor/diffs/words.js +47 -0
- package/build/components/editor/index.d.ts +21 -0
- package/build/components/editor/index.js +11 -0
- package/build/components/editor/markdown/editing.d.ts +7 -0
- package/build/components/editor/markdown/editing.js +55 -0
- package/build/components/editor/markdown/html.d.ts +24 -0
- package/build/components/editor/markdown/html.js +108 -0
- package/build/components/editor/markdown/index.d.ts +25 -0
- package/build/components/editor/markdown/index.js +33 -0
- package/build/components/editor/markdown/inline.d.ts +16 -0
- package/build/components/editor/markdown/inline.js +259 -0
- package/build/components/editor/markdown/layout.d.ts +63 -0
- package/build/components/editor/markdown/layout.js +252 -0
- package/build/components/editor/markdown/model.d.ts +53 -0
- package/build/components/editor/markdown/model.js +463 -0
- package/build/components/editor/markdown/render.d.ts +29 -0
- package/build/components/editor/markdown/render.js +561 -0
- package/build/components/editor/markdown/structure.d.ts +7 -0
- package/build/components/editor/markdown/structure.js +87 -0
- package/build/components/editor/markdown/view.d.ts +18 -0
- package/build/components/editor/markdown/view.js +1841 -0
- package/build/components/editor/tree/clipboard.d.ts +12 -0
- package/build/components/editor/tree/clipboard.js +48 -0
- package/build/components/editor/tree/compact.d.ts +23 -0
- package/build/components/editor/tree/compact.js +57 -0
- package/build/components/editor/tree/decorations.d.ts +39 -0
- package/build/components/editor/tree/decorations.js +126 -0
- package/build/components/editor/tree/drag.d.ts +45 -0
- package/build/components/editor/tree/drag.js +462 -0
- package/build/components/editor/tree/edit.d.ts +43 -0
- package/build/components/editor/tree/edit.js +134 -0
- package/build/components/editor/tree/filter.d.ts +7 -0
- package/build/components/editor/tree/filter.js +47 -0
- package/build/components/editor/tree/find.d.ts +55 -0
- package/build/components/editor/tree/find.js +291 -0
- package/build/components/editor/tree/glob.d.ts +2 -0
- package/build/components/editor/tree/glob.js +57 -0
- package/build/components/editor/tree/glyph.d.ts +6 -0
- package/build/components/editor/tree/glyph.js +31 -0
- package/build/components/editor/tree/history.d.ts +61 -0
- package/build/components/editor/tree/history.js +172 -0
- package/build/components/editor/tree/icons.d.ts +29 -0
- package/build/components/editor/tree/icons.js +417 -0
- package/build/components/editor/tree/index.d.ts +118 -0
- package/build/components/editor/tree/index.js +2227 -0
- package/build/components/editor/tree/jump.d.ts +8 -0
- package/build/components/editor/tree/jump.js +45 -0
- package/build/components/editor/tree/lazy.d.ts +24 -0
- package/build/components/editor/tree/lazy.js +71 -0
- package/build/components/editor/tree/model.d.ts +33 -0
- package/build/components/editor/tree/model.js +128 -0
- package/build/components/editor/tree/selection.d.ts +13 -0
- package/build/components/editor/tree/selection.js +55 -0
- package/build/components/editor/tree/shortcuts.d.ts +6 -0
- package/build/components/editor/tree/shortcuts.js +44 -0
- package/build/components/editor/tree/sort.d.ts +13 -0
- package/build/components/editor/tree/sort.js +53 -0
- package/build/components/editor/tree/spoken.d.ts +9 -0
- package/build/components/editor/tree/spoken.js +38 -0
- package/build/components/editor/tree/sprites.d.ts +4 -0
- package/build/components/editor/tree/sprites.js +152 -0
- package/build/components/editor/tree/sticky.d.ts +11 -0
- package/build/components/editor/tree/sticky.js +102 -0
- package/build/components/editor/tree/transfer.d.ts +15 -0
- package/build/components/editor/tree/transfer.js +75 -0
- package/build/components/editor/workspace/index.d.ts +64 -0
- package/build/components/editor/workspace/index.js +1125 -0
- package/build/components/editor/workspace/model.d.ts +161 -0
- package/build/components/editor/workspace/model.js +690 -0
- package/build/components/error/index.d.ts +31 -0
- package/build/components/error/index.js +150 -0
- package/build/components/error/scss/index.scss +2 -0
- package/build/components/filter/index.d.ts +38 -0
- package/build/components/filter/index.js +307 -0
- package/build/components/filter/scss/index.scss +2 -0
- package/build/components/form/action.js +1 -2
- package/build/components/form/index.d.ts +10 -1
- package/build/components/form/input.d.ts +12 -2
- package/build/components/form/input.js +12 -7
- package/build/components/form/scss/index.scss +1 -1
- package/build/components/frame/scss/index.scss +2 -0
- package/build/components/grid/scss/index.scss +1 -1
- package/build/components/heatmap/index.d.ts +32 -0
- package/build/components/heatmap/index.js +209 -0
- package/build/components/heatmap/scss/index.scss +2 -0
- package/build/components/highlight/easing.d.ts +2 -0
- package/build/components/highlight/easing.js +19 -0
- package/build/components/highlight/index.d.ts +6 -0
- package/build/components/highlight/index.js +334 -112
- package/build/components/highlight/scss/index.scss +1 -1
- package/build/components/icon/index.js +1 -1
- package/build/components/icon/scss/index.scss +1 -1
- package/build/components/image/index.d.ts +17 -12
- package/build/components/image/index.js +3 -61
- package/build/components/image/lazyload.d.ts +15 -0
- package/build/components/image/lazyload.js +72 -0
- package/build/components/image/scss/index.scss +1 -1
- package/build/components/index.d.ts +15 -79
- package/build/components/index.js +15 -79
- package/build/components/inline-edit/format.d.ts +29 -0
- package/build/components/inline-edit/format.js +112 -0
- package/build/components/inline-edit/history.d.ts +14 -0
- package/build/components/inline-edit/history.js +40 -0
- package/build/components/inline-edit/index.d.ts +13 -7
- package/build/components/inline-edit/index.js +44 -38
- package/build/components/inline-edit/markdown.d.ts +7 -0
- package/build/components/inline-edit/markdown.js +314 -0
- package/build/components/inline-edit/model.d.ts +61 -0
- package/build/components/inline-edit/model.js +192 -0
- package/build/components/inline-edit/rich.d.ts +39 -0
- package/build/components/inline-edit/rich.js +1236 -0
- package/build/components/inline-edit/scss/index.scss +1 -1
- package/build/components/inline-edit/selection.d.ts +7 -0
- package/build/components/inline-edit/selection.js +114 -0
- package/build/components/inline-edit/status.d.ts +24 -0
- package/build/components/inline-edit/status.js +196 -0
- package/build/components/inline-edit/utilities.d.ts +4 -0
- package/build/components/inline-edit/utilities.js +12 -0
- package/build/components/input/field.d.ts +14 -0
- package/build/components/input/field.js +34 -0
- package/build/components/input/index.d.ts +7 -14
- package/build/components/input/index.js +5 -41
- package/build/components/input/scrub.d.ts +30 -0
- package/build/components/input/scrub.js +221 -0
- package/build/components/input/scss/index.scss +1 -1
- package/build/components/input/tag.d.ts +24 -0
- package/build/components/input/tag.js +288 -0
- package/build/components/json/download.js +4 -3
- package/build/components/link/scss/index.scss +1 -1
- package/build/components/loading/index.d.ts +1 -1
- package/build/components/loading/scss/index.scss +1 -1
- package/build/components/marquee/index.d.ts +10 -1
- package/build/components/marquee/index.js +240 -174
- package/build/components/marquee/scss/index.scss +1 -1
- package/build/components/marquee/velocity.d.ts +17 -0
- package/build/components/marquee/velocity.js +78 -0
- package/build/components/metric/flash.d.ts +11 -0
- package/build/components/metric/flash.js +116 -0
- package/build/components/metric/index.d.ts +33 -0
- package/build/components/metric/index.js +271 -0
- package/build/components/metric/scss/index.scss +2 -0
- package/build/components/nav/index.d.ts +13 -0
- package/build/components/nav/index.js +6 -0
- package/build/components/nav/scss/index.scss +2 -0
- package/build/components/nav/spy.d.ts +9 -0
- package/build/components/nav/spy.js +157 -0
- package/build/components/nav/toc.d.ts +26 -0
- package/build/components/nav/toc.js +22 -0
- package/build/components/nav/tree.d.ts +39 -0
- package/build/components/nav/tree.js +123 -0
- package/build/components/notification/index.d.ts +15 -0
- package/build/components/notification/index.js +98 -0
- package/build/components/notification/scss/index.scss +2 -0
- package/build/components/overlay/index.d.ts +28 -9
- package/build/components/overlay/index.js +346 -8
- package/build/components/overlay/popup.d.ts +31 -0
- package/build/components/overlay/popup.js +150 -0
- package/build/components/overlay/scss/index.scss +1 -1
- package/build/components/page/scss/index.scss +1 -1
- package/build/components/pagination/dots.d.ts +21 -0
- package/build/components/pagination/dots.js +110 -0
- package/build/components/pagination/index.d.ts +8 -3
- package/build/components/pagination/index.js +36 -59
- package/build/components/pagination/scss/index.scss +1 -1
- package/build/components/progress/index.d.ts +7 -2
- package/build/components/progress/index.js +14 -10
- package/build/components/progress/scss/index.scss +1 -1
- package/build/components/progress/text.d.ts +12 -0
- package/build/components/progress/text.js +124 -0
- package/build/components/pull-to-refresh/index.d.ts +3 -11
- package/build/components/pull-to-refresh/index.js +127 -165
- package/build/components/pull-to-refresh/scss/index.scss +1 -1
- package/build/components/radio/index.d.ts +2 -2
- package/build/components/radio/index.js +2 -2
- package/build/components/range/filter.d.ts +22 -0
- package/build/components/range/filter.js +322 -0
- package/build/components/range/index.d.ts +9 -2
- package/build/components/range/index.js +13 -9
- package/build/components/range/scss/index.scss +1 -1
- package/build/components/relative-time/index.d.ts +15 -8
- package/build/components/relative-time/index.js +206 -193
- package/build/components/relative-time/scss/index.scss +1 -1
- package/build/components/root/scss/index.scss +1 -1
- package/build/components/select/index.d.ts +35 -16
- package/build/components/select/index.js +358 -92
- package/build/components/select/placement.d.ts +15 -0
- package/build/components/select/placement.js +31 -0
- package/build/components/select/scss/index.scss +1 -1
- package/build/components/shortcut-recorder/index.d.ts +7 -2
- package/build/components/shortcut-recorder/index.js +107 -99
- package/build/components/shortcut-recorder/scss/index.scss +1 -1
- package/build/components/sortable/index.d.ts +722 -4
- package/build/components/sortable/index.js +329 -213
- package/build/components/sortable/scss/index.scss +1 -1
- package/build/components/sticky-header/index.d.ts +22 -15
- package/build/components/sticky-header/index.js +41 -81
- package/build/components/sticky-header/scss/index.scss +1 -1
- package/build/components/story/index.d.ts +21 -0
- package/build/components/story/index.js +253 -0
- package/build/components/story/scss/index.scss +2 -0
- package/build/components/surface-field/engine.d.ts +65 -0
- package/build/components/surface-field/engine.js +970 -0
- package/build/components/surface-field/index.d.ts +2247 -1470
- package/build/components/surface-field/index.js +394 -371
- package/build/components/surface-field/scss/index.scss +1 -1
- package/build/components/surface-field/surface.d.ts +39 -0
- package/build/components/surface-field/surface.js +159 -0
- package/build/components/surface-field/worker/index.d.ts +9 -0
- package/build/components/surface-field/worker/index.js +19 -0
- package/build/components/switch/index.d.ts +2 -2
- package/build/components/switch/index.js +2 -2
- package/build/components/tasklist/index.d.ts +25 -717
- package/build/components/tasklist/index.js +151 -97
- package/build/components/tasklist/scss/index.scss +1 -1
- package/build/components/text/scss/index.scss +1 -1
- package/build/components/textarea/index.d.ts +15 -3
- package/build/components/textarea/index.js +124 -32
- package/build/components/textarea/scss/index.scss +1 -1
- package/build/components/toast/index.d.ts +40 -7
- package/build/components/toast/index.js +192 -145
- package/build/components/toast/scss/index.scss +1 -1
- package/build/components/tooltip/index.d.ts +3 -4
- package/build/components/tooltip/index.js +3 -4
- package/build/components/tooltip/menu.d.ts +47 -21
- package/build/components/tooltip/menu.js +525 -18
- package/build/components/tooltip/onclick.d.ts +1 -0
- package/build/components/tooltip/onclick.js +50 -33
- package/build/components/tooltip/onhover.d.ts +721 -10
- package/build/components/tooltip/onhover.js +129 -43
- package/build/components/tooltip/scss/index.scss +1 -1
- package/build/components/tooltip/shared.d.ts +34 -0
- package/build/components/tooltip/shared.js +569 -0
- package/build/components/tooltip/utilities.d.ts +23 -3
- package/build/components/tooltip/utilities.js +46 -21
- package/build/components/typewriter/index.d.ts +1439 -10
- package/build/components/typewriter/index.js +7 -2
- package/build/components/typewriter/retype.d.ts +7 -0
- package/build/components/typewriter/retype.js +83 -0
- package/build/components/typewriter/rotate.d.ts +13 -0
- package/build/components/typewriter/rotate.js +186 -0
- package/build/components/typewriter/scss/index.scss +1 -1
- package/build/components/uptime/index.js +61 -61
- package/build/components/uptime/scss/index.scss +1 -1
- package/build/components/usage-meter/index.d.ts +15 -3
- package/build/components/usage-meter/index.js +19 -19
- package/build/components/usage-meter/scss/index.scss +1 -1
- package/build/css-utilities/background/scss/index.scss +1 -1
- package/build/css-utilities/border/scss/index.scss +1 -1
- package/build/css-utilities/color/scss/index.scss +1 -1
- package/build/css-utilities/flex/scss/index.scss +1 -1
- package/build/css-utilities/font/geist/scss/index.scss +1 -1
- package/build/css-utilities/font/montserrat/scss/index.scss +1 -1
- package/build/css-utilities/glass/index.d.ts +10 -0
- package/build/css-utilities/glass/index.js +26 -0
- package/build/css-utilities/glass/scss/index.scss +1 -1
- package/build/css-utilities/index.d.ts +3 -0
- package/build/css-utilities/index.js +3 -0
- package/build/css-utilities/index.scss +1 -1
- package/build/css-utilities/inline/scss/index.scss +1 -1
- package/build/css-utilities/scrollbar/index.d.ts +42 -0
- package/build/css-utilities/scrollbar/index.js +155 -0
- package/build/css-utilities/scrollbar/scss/index.scss +1 -1
- package/build/css-utilities/skeleton/scss/index.scss +1 -1
- package/build/css-utilities/text/scss/index.scss +1 -1
- package/build/css-utilities/theme/index.d.ts +16 -0
- package/build/css-utilities/theme/index.js +60 -0
- package/build/normalize/scss/index.scss +3 -0
- package/build/shared/anchor.d.ts +7 -0
- package/build/shared/anchor.js +18 -0
- package/build/shared/animation.d.ts +6 -0
- package/build/shared/animation.js +57 -0
- package/build/shared/clamp.d.ts +2 -0
- package/build/shared/clamp.js +4 -0
- package/build/shared/deadline.d.ts +2 -0
- package/build/shared/deadline.js +94 -0
- package/build/shared/dismiss.d.ts +2 -0
- package/build/shared/dismiss.js +5 -0
- package/build/shared/drag.d.ts +47 -0
- package/build/shared/drag.js +189 -0
- package/build/shared/fuzzy.d.ts +6 -0
- package/build/shared/fuzzy.js +153 -0
- package/build/shared/keystep.d.ts +2 -0
- package/build/shared/keystep.js +21 -0
- package/build/shared/platform.d.ts +2 -0
- package/build/shared/platform.js +3 -0
- package/build/shared/press.d.ts +15 -0
- package/build/shared/press.js +83 -0
- package/build/shared/resize.d.ts +18 -0
- package/build/shared/resize.js +137 -0
- package/build/shared/subscribe.d.ts +3 -0
- package/build/shared/subscribe.js +12 -0
- package/build/shared/ticker.d.ts +3 -0
- package/build/shared/ticker.js +34 -0
- package/build/shared/transition.d.ts +11 -0
- package/build/shared/transition.js +49 -0
- package/build/shared/viewport.d.ts +7 -0
- package/build/shared/viewport.js +11 -0
- package/build/shared/visible.d.ts +8 -0
- package/build/shared/visible.js +79 -0
- package/build/themes/dark/input.d.ts +3 -721
- package/build/themes/dark/input.js +4 -2
- package/build/themes/dark/select.js +2 -1
- package/build/themes/dark/textarea.d.ts +15 -3
- package/build/themes/dark/textarea.js +1 -0
- package/package.json +11 -4
- package/pnpm-workspace.yaml +3 -18
- package/scripts/animation.test.mjs +45 -0
- package/scripts/chat-minimap.test.mjs +53 -0
- package/scripts/code-editor-features.test.mjs +287 -0
- package/scripts/code-editor-markdown.test.mjs +527 -0
- package/scripts/code-editor-services.test.mjs +361 -0
- package/scripts/code-editor-workspace.test.mjs +398 -0
- package/scripts/code-editor.test.mjs +1615 -0
- package/scripts/css-variables.test.mjs +1 -1
- package/scripts/deadline.test.mjs +191 -0
- package/scripts/diffs.test.mjs +189 -0
- package/scripts/file-tree-features.test.mjs +137 -0
- package/scripts/file-tree-icons.test.mjs +234 -0
- package/scripts/highlight.test.mjs +52 -0
- package/scripts/resize.test.mjs +247 -0
- package/scripts/resolve.mjs +52 -0
- package/scripts/slides.test.mjs +126 -0
- package/scripts/subscribe.test.mjs +49 -0
- package/scripts/ticker.test.mjs +126 -0
- package/scripts/transition.test.mjs +83 -0
- package/scripts/viewport.test.mjs +82 -0
- package/scripts/visible.test.mjs +186 -0
- package/shared.scss +2 -0
- package/src/components/accordion/index.ts +71 -12
- package/src/components/accordion/scss/index.scss +32 -129
- package/src/components/accordion/scss/variables.scss +7 -0
- package/src/components/announcement/index.ts +26 -0
- package/src/components/announcement/scss/index.scss +47 -0
- package/src/components/announcement/scss/variables.scss +23 -0
- package/src/components/banner/scss/index.scss +4 -8
- package/src/components/banner/scss/variables.scss +6 -0
- package/src/components/breadcrumb/index.ts +222 -25
- package/src/components/breadcrumb/scss/index.scss +217 -34
- package/src/components/breadcrumb/scss/variables.scss +73 -8
- package/src/components/button/copy.ts +1 -1
- package/src/components/button/faces.ts +1 -2
- package/src/components/button/fan.ts +111 -114
- package/src/components/button/hold.ts +9 -9
- package/src/components/button/icons.ts +15 -32
- package/src/components/button/index.ts +13 -1
- package/src/components/button/kbd.ts +6 -6
- package/src/components/button/loading.ts +1 -1
- package/src/components/button/morph.ts +153 -0
- package/src/components/button/scss/_kbd.scss +2 -1
- package/src/components/button/scss/_morph.scss +119 -0
- package/src/components/button/scss/_sidebar.scss +50 -0
- package/src/components/button/scss/_split.scss +53 -0
- package/src/components/button/scss/index.scss +45 -31
- package/src/components/button/scss/variables.scss +85 -34
- package/src/components/button/sidebar.ts +60 -0
- package/src/components/button/split.ts +60 -0
- package/src/components/capslock/index.ts +54 -0
- package/src/components/capslock/scss/index.scss +55 -0
- package/src/components/capslock/scss/variables.scss +26 -0
- package/src/components/card/expand.ts +141 -0
- package/src/components/card/index.ts +8 -0
- package/src/components/card/scss/_expand.scss +240 -0
- package/src/components/card/scss/index.scss +52 -9
- package/src/components/card/scss/variables.scss +85 -19
- package/src/components/cc/index.ts +558 -0
- package/src/components/cc/scss/index.scss +429 -0
- package/src/components/cc/scss/variables.scss +68 -0
- package/src/components/chat-minimap/index.ts +265 -0
- package/src/components/chat-minimap/range.ts +59 -0
- package/src/components/chat-minimap/scss/index.scss +140 -0
- package/src/components/chat-minimap/scss/variables.scss +97 -0
- package/src/components/chat-minimap/spy.ts +151 -0
- package/src/components/checkbox/group.ts +216 -0
- package/src/components/checkbox/index.ts +3 -41
- package/src/components/checkbox/options.ts +55 -0
- package/src/components/checkbox/scss/index.scss +69 -17
- package/src/components/checkbox/scss/variables.scss +28 -7
- package/src/components/clipboard/copy.ts +5 -5
- package/src/components/color-picker/index.ts +160 -233
- package/src/components/color-picker/scss/index.scss +51 -93
- package/src/components/color-picker/scss/variables.scss +68 -10
- package/src/components/command/index.ts +788 -206
- package/src/components/command/scss/index.scss +168 -158
- package/src/components/command/scss/variables.scss +118 -25
- package/src/components/copy/index.ts +69 -0
- package/src/components/copy/scss/index.scss +52 -0
- package/src/components/copy/scss/variables.scss +22 -0
- package/src/components/counter/index.ts +69 -94
- package/src/components/counter/scss/index.scss +20 -48
- package/src/components/counter/scss/variables.scss +24 -35
- package/src/components/datalist/index.ts +49 -29
- package/src/components/datalist/scss/index.scss +43 -24
- package/src/components/datalist/scss/variables.scss +7 -4
- package/src/components/dock/index.ts +61 -231
- package/src/components/dock/scss/index.scss +73 -39
- package/src/components/dock/scss/variables.scss +77 -16
- package/src/components/editor/code/changes.ts +111 -0
- package/src/components/editor/code/clipboard.ts +81 -0
- package/src/components/editor/code/colors.ts +278 -0
- package/src/components/editor/code/commands.ts +879 -0
- package/src/components/editor/code/composition.ts +85 -0
- package/src/components/editor/code/conflicts.ts +160 -0
- package/src/components/editor/code/document.ts +789 -0
- package/src/components/editor/code/editor.ts +68 -0
- package/src/components/editor/code/features.ts +1144 -0
- package/src/components/editor/code/field.ts +96 -0
- package/src/components/editor/code/find.ts +340 -0
- package/src/components/editor/code/folding.ts +689 -0
- package/src/components/editor/code/goto.ts +89 -0
- package/src/components/editor/code/gutter.ts +47 -0
- package/src/components/editor/code/index.ts +29 -0
- package/src/components/editor/code/keymap.ts +518 -0
- package/src/components/editor/code/layout.ts +565 -0
- package/src/components/editor/code/links.ts +55 -0
- package/src/components/editor/code/minimap.ts +260 -0
- package/src/components/editor/code/navigation.ts +95 -0
- package/src/components/editor/code/pointer.ts +199 -0
- package/src/components/editor/code/projection.ts +342 -0
- package/src/components/editor/code/rows.ts +412 -0
- package/src/components/editor/code/scss/_editor.scss +432 -0
- package/src/components/editor/code/scss/_features.scss +311 -0
- package/src/components/editor/code/scss/_services.scss +154 -0
- package/src/components/editor/code/scss/index.scss +6 -0
- package/src/components/editor/code/scss/variables.scss +194 -0
- package/src/components/editor/code/search.ts +391 -0
- package/src/components/editor/code/selection.ts +83 -0
- package/src/components/editor/code/services/completion.ts +114 -0
- package/src/components/editor/code/services/diagnostics.ts +168 -0
- package/src/components/editor/code/services/hover.ts +74 -0
- package/src/components/editor/code/services/index.ts +118 -0
- package/src/components/editor/code/services/model.ts +270 -0
- package/src/components/editor/code/services/protocol.ts +55 -0
- package/src/components/editor/code/services/session.ts +721 -0
- package/src/components/editor/code/services/transport.ts +36 -0
- package/src/components/editor/code/syntax.ts +1673 -0
- package/src/components/editor/code/unicode.ts +156 -0
- package/src/components/editor/code/view.ts +2139 -0
- package/src/components/editor/diffs/diff3.ts +280 -0
- package/src/components/editor/diffs/engine.ts +192 -0
- package/src/components/editor/diffs/hunks.ts +196 -0
- package/src/components/editor/diffs/index.ts +20 -0
- package/src/components/editor/diffs/merge.ts +377 -0
- package/src/components/editor/diffs/render.ts +95 -0
- package/src/components/editor/diffs/scss/_diffs.scss +352 -0
- package/src/components/editor/diffs/scss/_merge.scss +121 -0
- package/src/components/editor/diffs/scss/index.scss +5 -0
- package/src/components/editor/diffs/scss/variables.scss +70 -0
- package/src/components/editor/diffs/view.ts +464 -0
- package/src/components/editor/diffs/words.ts +85 -0
- package/src/components/editor/index.ts +59 -0
- package/src/components/editor/markdown/editing.ts +92 -0
- package/src/components/editor/markdown/html.ts +192 -0
- package/src/components/editor/markdown/index.ts +72 -0
- package/src/components/editor/markdown/inline.ts +399 -0
- package/src/components/editor/markdown/layout.ts +446 -0
- package/src/components/editor/markdown/model.ts +737 -0
- package/src/components/editor/markdown/render.ts +461 -0
- package/src/components/editor/markdown/scss/index.scss +456 -0
- package/src/components/editor/markdown/scss/variables.scss +109 -0
- package/src/components/editor/markdown/structure.ts +136 -0
- package/src/components/editor/markdown/view.ts +2417 -0
- package/src/components/editor/tree/clipboard.ts +80 -0
- package/src/components/editor/tree/compact.ts +93 -0
- package/src/components/editor/tree/decorations.ts +210 -0
- package/src/components/editor/tree/drag.ts +652 -0
- package/src/components/editor/tree/edit.ts +234 -0
- package/src/components/editor/tree/filter.ts +81 -0
- package/src/components/editor/tree/find.ts +500 -0
- package/src/components/editor/tree/glob.ts +76 -0
- package/src/components/editor/tree/glyph.ts +49 -0
- package/src/components/editor/tree/history.ts +282 -0
- package/src/components/editor/tree/icons.ts +507 -0
- package/src/components/editor/tree/index.ts +3438 -0
- package/src/components/editor/tree/jump.ts +78 -0
- package/src/components/editor/tree/lazy.ts +111 -0
- package/src/components/editor/tree/model.ts +235 -0
- package/src/components/editor/tree/scss/index.scss +732 -0
- package/src/components/editor/tree/scss/variables.scss +260 -0
- package/src/components/editor/tree/selection.ts +85 -0
- package/src/components/editor/tree/shortcuts.ts +64 -0
- package/src/components/editor/tree/sort.ts +104 -0
- package/src/components/editor/tree/spoken.ts +67 -0
- package/src/components/editor/tree/sprites.ts +159 -0
- package/src/components/editor/tree/sticky.ts +154 -0
- package/src/components/editor/tree/transfer.ts +134 -0
- package/src/components/editor/workspace/index.ts +1357 -0
- package/src/components/editor/workspace/model.ts +1023 -0
- package/src/components/editor/workspace/scss/index.scss +547 -0
- package/src/components/error/index.ts +189 -0
- package/src/components/error/scss/index.scss +89 -0
- package/src/components/error/scss/variables.scss +13 -0
- package/src/components/filter/index.ts +420 -0
- package/src/components/filter/scss/index.scss +6 -0
- package/src/components/filter/scss/variables.scss +17 -0
- package/src/components/form/action.ts +4 -4
- package/src/components/form/input.ts +13 -7
- package/src/components/frame/scss/index.scss +107 -0
- package/src/components/frame/scss/variables.scss +27 -0
- package/src/components/heatmap/index.ts +281 -0
- package/src/components/heatmap/scss/index.scss +131 -0
- package/src/components/heatmap/scss/variables.scss +58 -0
- package/src/components/highlight/easing.ts +33 -0
- package/src/components/highlight/index.ts +580 -77
- package/src/components/highlight/scss/index.scss +49 -9
- package/src/components/highlight/scss/variables.scss +62 -3
- package/src/components/icon/scss/index.scss +0 -1
- package/src/components/image/index.ts +3 -76
- package/src/components/image/lazyload.ts +95 -0
- package/src/components/image/scss/index.scss +71 -57
- package/src/components/image/scss/variables.scss +39 -14
- package/src/components/index.ts +27 -79
- package/src/components/inline-edit/format.ts +183 -0
- package/src/components/inline-edit/history.ts +71 -0
- package/src/components/inline-edit/index.ts +44 -38
- package/src/components/inline-edit/markdown.ts +485 -0
- package/src/components/inline-edit/model.ts +366 -0
- package/src/components/inline-edit/rich.ts +1518 -0
- package/src/components/inline-edit/scss/index.scss +431 -41
- package/src/components/inline-edit/scss/variables.scss +105 -0
- package/src/components/inline-edit/selection.ts +188 -0
- package/src/components/inline-edit/status.ts +194 -0
- package/src/components/inline-edit/utilities.ts +18 -0
- package/src/components/input/field.ts +32 -0
- package/src/components/input/index.ts +5 -38
- package/src/components/input/scrub.ts +338 -0
- package/src/components/input/scss/_field.scss +53 -0
- package/src/components/input/scss/_scrub.scss +91 -0
- package/src/components/input/scss/_tag.scss +162 -0
- package/src/components/input/scss/index.scss +5 -18
- package/src/components/input/scss/variables.scss +108 -38
- package/src/components/input/tag.ts +403 -0
- package/src/components/json/download.ts +9 -6
- package/src/components/link/scss/index.scss +9 -4
- package/src/components/link/scss/variables.scss +1 -31
- package/src/components/loading/index.ts +1 -1
- package/src/components/loading/scss/index.scss +3 -6
- package/src/components/marquee/index.ts +246 -169
- package/src/components/marquee/scss/index.scss +21 -28
- package/src/components/marquee/scss/variables.scss +16 -0
- package/src/components/marquee/velocity.ts +144 -0
- package/src/components/metric/flash.ts +128 -0
- package/src/components/metric/index.ts +315 -0
- package/src/components/metric/scss/_flash.scss +163 -0
- package/src/components/metric/scss/index.scss +163 -0
- package/src/components/metric/scss/variables.scss +133 -0
- package/src/components/nav/index.ts +13 -0
- package/src/components/nav/scss/index.scss +123 -0
- package/src/components/nav/scss/variables.scss +60 -0
- package/src/components/nav/spy.ts +203 -0
- package/src/components/nav/toc.ts +48 -0
- package/src/components/nav/tree.ts +181 -0
- package/src/components/notification/index.ts +111 -0
- package/src/components/notification/scss/index.scss +123 -0
- package/src/components/notification/scss/variables.scss +54 -0
- package/src/components/overlay/index.ts +487 -5
- package/src/components/overlay/popup.ts +214 -0
- package/src/components/overlay/scss/index.scss +379 -7
- package/src/components/overlay/scss/variables.scss +117 -0
- package/src/components/pagination/dots.ts +132 -0
- package/src/components/pagination/index.ts +34 -45
- package/src/components/pagination/scss/_dots.scss +67 -0
- package/src/components/pagination/scss/index.scss +15 -8
- package/src/components/pagination/scss/variables.scss +59 -0
- package/src/components/progress/index.ts +18 -8
- package/src/components/progress/scss/_text.scss +132 -0
- package/src/components/progress/scss/index.scss +1 -0
- package/src/components/progress/scss/variables.scss +42 -0
- package/src/components/progress/text.ts +158 -0
- package/src/components/pull-to-refresh/index.ts +126 -248
- package/src/components/pull-to-refresh/scss/index.scss +65 -29
- package/src/components/pull-to-refresh/scss/variables.scss +24 -1
- package/src/components/radio/index.ts +3 -3
- package/src/components/range/filter.ts +398 -0
- package/src/components/range/index.ts +27 -16
- package/src/components/range/scss/_filter.scss +374 -0
- package/src/components/range/scss/_slider.scss +140 -0
- package/src/components/range/scss/_vertical.scss +22 -0
- package/src/components/range/scss/index.scss +26 -14
- package/src/components/range/scss/variables.scss +78 -13
- package/src/components/relative-time/index.ts +218 -233
- package/src/components/relative-time/scss/index.scss +42 -18
- package/src/components/relative-time/scss/variables.scss +25 -0
- package/src/components/root/scss/index.scss +7 -3
- package/src/components/root/scss/variables.scss +21 -12
- package/src/components/select/index.ts +506 -153
- package/src/components/select/placement.ts +75 -0
- package/src/components/select/scss/index.scss +182 -45
- package/src/components/select/scss/variables.scss +58 -44
- package/src/components/shortcut-recorder/index.ts +145 -106
- package/src/components/shortcut-recorder/scss/index.scss +34 -121
- package/src/components/shortcut-recorder/scss/variables.scss +27 -39
- package/src/components/sortable/index.ts +380 -272
- package/src/components/sortable/scss/index.scss +107 -38
- package/src/components/sortable/scss/variables.scss +12 -1
- package/src/components/sticky-header/index.ts +43 -92
- package/src/components/sticky-header/scss/index.scss +76 -86
- package/src/components/sticky-header/scss/variables.scss +36 -12
- package/src/components/story/index.ts +305 -0
- package/src/components/story/scss/index.scss +189 -0
- package/src/components/story/scss/variables.scss +28 -0
- package/src/components/surface-field/engine.ts +1631 -0
- package/src/components/surface-field/index.ts +574 -608
- package/src/components/surface-field/scss/index.scss +74 -17
- package/src/components/surface-field/scss/variables.scss +23 -0
- package/src/components/surface-field/surface.ts +260 -0
- package/src/components/surface-field/worker/index.ts +41 -0
- package/src/components/switch/index.ts +3 -3
- package/src/components/tasklist/index.ts +197 -119
- package/src/components/tasklist/scss/index.scss +45 -128
- package/src/components/tasklist/scss/variables.scss +97 -70
- package/src/components/text/scss/index.scss +2 -15
- package/src/components/textarea/index.ts +142 -39
- package/src/components/textarea/scss/index.scss +41 -18
- package/src/components/textarea/scss/variables.scss +5 -46
- package/src/components/toast/index.ts +270 -157
- package/src/components/toast/scss/index.scss +244 -225
- package/src/components/toast/scss/variables.scss +68 -9
- package/src/components/tooltip/index.ts +9 -4
- package/src/components/tooltip/menu.ts +599 -37
- package/src/components/tooltip/onclick.ts +28 -22
- package/src/components/tooltip/onhover.ts +155 -73
- package/src/components/tooltip/scss/_menu.scss +352 -0
- package/src/components/tooltip/scss/_shared.scss +109 -0
- package/src/components/tooltip/scss/_south.scss +0 -1
- package/src/components/tooltip/scss/index.scss +16 -97
- package/src/components/tooltip/scss/variables.scss +103 -59
- package/src/components/tooltip/shared.ts +819 -0
- package/src/components/tooltip/utilities.ts +78 -23
- package/src/components/typewriter/index.ts +8 -1
- package/src/components/typewriter/retype.ts +99 -0
- package/src/components/typewriter/rotate.ts +279 -0
- package/src/components/typewriter/scss/_caret.scss +34 -0
- package/src/components/typewriter/scss/_retype.scss +67 -0
- package/src/components/typewriter/scss/_rotate.scss +28 -0
- package/src/components/typewriter/scss/index.scss +3 -33
- package/src/components/typewriter/scss/variables.scss +60 -10
- package/src/components/uptime/index.ts +50 -49
- package/src/components/uptime/scss/index.scss +67 -47
- package/src/components/uptime/scss/variables.scss +32 -4
- package/src/components/usage-meter/index.ts +17 -17
- package/src/components/usage-meter/scss/index.scss +19 -31
- package/src/components/usage-meter/scss/variables.scss +15 -7
- package/src/css-utilities/background/scss/index.scss +1 -1
- package/src/css-utilities/background/scss/variables.scss +2 -17
- package/src/css-utilities/border/scss/index.scss +1 -1
- package/src/css-utilities/border/scss/variables.scss +7 -23
- package/src/css-utilities/color/scss/index.scss +1 -1
- package/src/css-utilities/color/scss/variables.scss +2 -17
- package/src/css-utilities/flex/scss/variables.scss +0 -1
- package/src/css-utilities/font/geist/scss/index.scss +2 -2
- package/src/css-utilities/font/montserrat/scss/index.scss +2 -2
- package/src/css-utilities/glass/index.ts +42 -0
- package/src/css-utilities/glass/scss/index.scss +65 -1
- package/src/css-utilities/glass/scss/variables.scss +6 -1
- package/src/css-utilities/index.scss +0 -5
- package/src/css-utilities/index.ts +3 -0
- package/src/css-utilities/scrollbar/index.ts +200 -0
- package/src/css-utilities/scrollbar/scss/index.scss +300 -36
- package/src/css-utilities/scrollbar/scss/variables.scss +38 -3
- package/src/css-utilities/text/scss/index.scss +97 -6
- package/src/css-utilities/text/scss/variables.scss +22 -0
- package/src/css-utilities/theme/index.ts +71 -0
- package/src/normalize/scss/index.scss +42 -0
- package/src/shared/anchor.ts +34 -0
- package/src/shared/animation.ts +106 -0
- package/src/shared/clamp.ts +6 -0
- package/src/shared/deadline.ts +153 -0
- package/src/shared/dismiss.ts +6 -0
- package/src/shared/drag.ts +348 -0
- package/src/shared/fuzzy.ts +263 -0
- package/src/shared/keystep.ts +28 -0
- package/src/shared/platform.ts +10 -0
- package/src/shared/press.ts +144 -0
- package/src/shared/resize.ts +232 -0
- package/src/shared/scss/absolute.scss +28 -0
- package/src/shared/scss/anchor.scss +63 -0
- package/src/shared/scss/background.scss +21 -0
- package/src/shared/scss/border.scss +21 -0
- package/src/shared/scss/color.scss +21 -0
- package/src/shared/scss/drag.scss +7 -0
- package/src/shared/scss/hidden.scss +22 -0
- package/src/shared/scss/icon.scss +36 -0
- package/src/shared/scss/index.scss +16 -0
- package/src/shared/scss/layer.scss +20 -0
- package/src/shared/scss/list.scss +64 -0
- package/src/shared/scss/reset.scss +21 -0
- package/src/shared/scss/string.scss +18 -0
- package/src/shared/scss/text.scss +31 -0
- package/src/shared/subscribe.ts +20 -0
- package/src/shared/ticker.ts +56 -0
- package/src/shared/transition.ts +61 -0
- package/src/shared/viewport.ts +23 -0
- package/src/shared/visible.ts +127 -0
- package/src/themes/dark/input.ts +6 -2
- package/src/themes/dark/select.ts +3 -2
- package/src/themes/dark/textarea.ts +1 -0
- package/src/tokens/scss/box-shadow.scss +12 -16
- package/src/tokens/scss/color.scss +3 -2
- package/src/tokens/scss/font-family.scss +5 -0
- package/src/tokens/scss/index.scss +10 -0
- package/src/tokens/scss/state.scss +10 -8
- package/storage/code.md +180 -0
- package/storage/editor-features.md +227 -0
- package/storage/svg/amex.svg +1 -0
- package/storage/svg/arrow-down.svg +1 -0
- package/storage/svg/arrow-up.svg +1 -0
- package/storage/svg/bold.svg +1 -0
- package/storage/svg/caps-lock.svg +1 -0
- package/storage/svg/card.svg +1 -0
- package/storage/svg/chevron-down.svg +1 -0
- package/storage/svg/chevron-left.svg +1 -0
- package/storage/svg/chevron-right.svg +1 -0
- package/storage/svg/chevron-up.svg +1 -0
- package/storage/svg/chevrons-up-down.svg +1 -0
- package/storage/svg/chip.svg +1 -0
- package/storage/svg/circle.svg +1 -0
- package/storage/svg/clear-format.svg +1 -0
- package/storage/svg/close.svg +1 -0
- package/storage/svg/code.svg +1 -0
- package/storage/svg/command.svg +1 -0
- package/storage/svg/contrast.svg +1 -0
- package/storage/svg/copy.svg +1 -0
- package/storage/svg/corner-down-right.svg +1 -0
- package/storage/svg/credit-card.svg +1 -0
- package/storage/svg/dash.svg +1 -0
- package/storage/svg/discover.svg +1 -0
- package/storage/svg/document.svg +1 -0
- package/storage/svg/dots.svg +1 -0
- package/storage/svg/download.svg +1 -0
- package/storage/svg/enter.svg +1 -0
- package/storage/svg/expand.svg +1 -0
- package/storage/svg/external-link.svg +1 -0
- package/storage/svg/eye-off.svg +1 -0
- package/storage/svg/eye.svg +1 -0
- package/storage/svg/file-agents.svg +1 -0
- package/storage/svg/file-astro.svg +1 -0
- package/storage/svg/file-babel.svg +1 -0
- package/storage/svg/file-bash.svg +1 -0
- package/storage/svg/file-biome.svg +1 -0
- package/storage/svg/file-bootstrap.svg +1 -0
- package/storage/svg/file-browserslist.svg +1 -0
- package/storage/svg/file-bun.svg +1 -0
- package/storage/svg/file-c.svg +1 -0
- package/storage/svg/file-claude.svg +1 -0
- package/storage/svg/file-css.svg +1 -0
- package/storage/svg/file-database.svg +1 -0
- package/storage/svg/file-docker.svg +1 -0
- package/storage/svg/file-eslint.svg +1 -0
- package/storage/svg/file-font.svg +1 -0
- package/storage/svg/file-git.svg +1 -0
- package/storage/svg/file-go.svg +1 -0
- package/storage/svg/file-graphql.svg +1 -0
- package/storage/svg/file-html.svg +1 -0
- package/storage/svg/file-image.svg +1 -0
- package/storage/svg/file-javascript.svg +1 -0
- package/storage/svg/file-json.svg +1 -0
- package/storage/svg/file-markdown.svg +1 -0
- package/storage/svg/file-mcp.svg +1 -0
- package/storage/svg/file-nextjs.svg +1 -0
- package/storage/svg/file-npm.svg +1 -0
- package/storage/svg/file-oxc.svg +1 -0
- package/storage/svg/file-package.svg +1 -0
- package/storage/svg/file-pnpm.svg +1 -0
- package/storage/svg/file-postcss.svg +1 -0
- package/storage/svg/file-prettier.svg +1 -0
- package/storage/svg/file-python.svg +1 -0
- package/storage/svg/file-react.svg +1 -0
- package/storage/svg/file-readme.svg +1 -0
- package/storage/svg/file-ruby.svg +1 -0
- package/storage/svg/file-rust.svg +1 -0
- package/storage/svg/file-sass.svg +1 -0
- package/storage/svg/file-stylelint.svg +1 -0
- package/storage/svg/file-svelte.svg +1 -0
- package/storage/svg/file-svg.svg +1 -0
- package/storage/svg/file-svgo.svg +1 -0
- package/storage/svg/file-swift.svg +1 -0
- package/storage/svg/file-table.svg +1 -0
- package/storage/svg/file-tailwind.svg +1 -0
- package/storage/svg/file-terraform.svg +1 -0
- package/storage/svg/file-text.svg +1 -0
- package/storage/svg/file-tsconfig.svg +1 -0
- package/storage/svg/file-typescript.svg +1 -0
- package/storage/svg/file-vite.svg +1 -0
- package/storage/svg/file-vscode.svg +1 -0
- package/storage/svg/file-vue.svg +1 -0
- package/storage/svg/file-wasm.svg +1 -0
- package/storage/svg/file-webpack.svg +1 -0
- package/storage/svg/file-yml.svg +1 -0
- package/storage/svg/file-zig.svg +1 -0
- package/storage/svg/file-zip.svg +1 -0
- package/storage/svg/file.svg +1 -0
- package/storage/svg/folder-assets-open.svg +1 -0
- package/storage/svg/folder-assets.svg +1 -0
- package/storage/svg/folder-components-open.svg +1 -0
- package/storage/svg/folder-components.svg +1 -0
- package/storage/svg/folder-docs-open.svg +1 -0
- package/storage/svg/folder-docs.svg +1 -0
- package/storage/svg/folder-git-open.svg +1 -0
- package/storage/svg/folder-git.svg +1 -0
- package/storage/svg/folder-open.svg +1 -0
- package/storage/svg/folder-output-open.svg +1 -0
- package/storage/svg/folder-output.svg +1 -0
- package/storage/svg/folder-packages-open.svg +1 -0
- package/storage/svg/folder-packages.svg +1 -0
- package/storage/svg/folder-public-open.svg +1 -0
- package/storage/svg/folder-public.svg +1 -0
- package/storage/svg/folder-scripts-open.svg +1 -0
- package/storage/svg/folder-scripts.svg +1 -0
- package/storage/svg/folder-src-open.svg +1 -0
- package/storage/svg/folder-src.svg +1 -0
- package/storage/svg/folder-test-open.svg +1 -0
- package/storage/svg/folder-test.svg +1 -0
- package/storage/svg/folder.svg +1 -0
- package/storage/svg/gear.svg +1 -0
- package/storage/svg/help.svg +1 -0
- package/storage/svg/highlight.svg +1 -0
- package/storage/svg/home.svg +1 -0
- package/storage/svg/inbox.svg +1 -0
- package/storage/svg/italic.svg +1 -0
- package/storage/svg/keyboard.svg +1 -0
- package/storage/svg/link.svg +1 -0
- package/storage/svg/list-checks.svg +1 -0
- package/storage/svg/lock.svg +1 -0
- package/storage/svg/log-out.svg +1 -0
- package/storage/svg/map.svg +1 -0
- package/storage/svg/mastercard.svg +1 -0
- package/storage/svg/pause.svg +1 -0
- package/storage/svg/pencil.svg +1 -0
- package/storage/svg/play.svg +1 -0
- package/storage/svg/plus.svg +1 -0
- package/storage/svg/redo.svg +1 -0
- package/storage/svg/save.svg +1 -0
- package/storage/svg/sidebar-filled.svg +1 -0
- package/storage/svg/sidebar.svg +1 -0
- package/storage/svg/slash.svg +1 -0
- package/storage/svg/sliders.svg +1 -0
- package/storage/svg/strikethrough.svg +1 -0
- package/storage/svg/trash.svg +1 -0
- package/storage/svg/triangle.svg +1 -0
- package/storage/svg/undo.svg +1 -0
- package/storage/svg/unlink.svg +1 -0
- package/storage/svg/user.svg +1 -0
- package/storage/svg/visa.svg +1 -0
- package/storage/svg/zoom-in.svg +1 -0
- package/storage/svg/zoom-out.svg +1 -0
- package/tokens.scss +1 -1
- package/vite.config.ts +47 -46
- package/build/components/accordion/more.d.ts +0 -20
- package/build/components/accordion/more.js +0 -76
- package/build/components/anchor/scss/index.scss +0 -2
- package/build/components/announcement-banner/index.d.ts +0 -33
- package/build/components/announcement-banner/index.js +0 -61
- package/build/components/announcement-banner/scss/index.scss +0 -2
- package/build/components/autosave/index.d.ts +0 -12
- package/build/components/autosave/index.js +0 -140
- package/build/components/autosave/scss/index.scss +0 -2
- package/build/components/back-to-top/index.d.ts +0 -16
- package/build/components/back-to-top/index.js +0 -182
- package/build/components/back-to-top/scss/index.scss +0 -2
- package/build/components/bento-grid/index.d.ts +0 -25
- package/build/components/bento-grid/index.js +0 -38
- package/build/components/bento-grid/scss/index.scss +0 -2
- package/build/components/bento-grid-spotlight/index.d.ts +0 -35
- package/build/components/bento-grid-spotlight/index.js +0 -108
- package/build/components/bento-grid-spotlight/scss/index.scss +0 -2
- package/build/components/bookmark-button/index.d.ts +0 -23
- package/build/components/bookmark-button/index.js +0 -120
- package/build/components/bookmark-button/scss/index.scss +0 -2
- package/build/components/breadcrumb/ellipsis.d.ts +0 -12
- package/build/components/breadcrumb/ellipsis.js +0 -11
- package/build/components/breadcrumb/item.d.ts +0 -12
- package/build/components/breadcrumb/item.js +0 -9
- package/build/components/breadcrumb/link.d.ts +0 -15
- package/build/components/breadcrumb/link.js +0 -14
- package/build/components/breadcrumb/list.d.ts +0 -12
- package/build/components/breadcrumb/list.js +0 -9
- package/build/components/breadcrumb/menu.d.ts +0 -13
- package/build/components/breadcrumb/menu.js +0 -16
- package/build/components/breadcrumb/page.d.ts +0 -12
- package/build/components/breadcrumb/page.js +0 -9
- package/build/components/breadcrumb/separator.d.ts +0 -12
- package/build/components/breadcrumb/separator.js +0 -11
- package/build/components/breadcrumb/svg/caret.svg +0 -1
- package/build/components/breadcrumb/svg/chevron.svg +0 -1
- package/build/components/breadcrumb/svg/more.svg +0 -1
- package/build/components/breadcrumbs/index.d.ts +0 -27
- package/build/components/breadcrumbs/index.js +0 -324
- package/build/components/breadcrumbs/scss/index.scss +0 -2
- package/build/components/button/svg/plus.svg +0 -1
- package/build/components/card-input/index.d.ts +0 -31
- package/build/components/card-input/index.js +0 -562
- package/build/components/card-input/scss/index.scss +0 -2
- package/build/components/carousel-3d/index.d.ts +0 -27
- package/build/components/carousel-3d/index.js +0 -248
- package/build/components/carousel-3d/scss/index.scss +0 -2
- package/build/components/checkbox-group/index.d.ts +0 -30
- package/build/components/checkbox-group/index.js +0 -101
- package/build/components/checkbox-group/scss/index.scss +0 -2
- package/build/components/code-morph/index.d.ts +0 -25
- package/build/components/code-morph/index.js +0 -401
- package/build/components/code-morph/scss/index.scss +0 -2
- package/build/components/collapsible-sidebar/index.d.ts +0 -41
- package/build/components/collapsible-sidebar/index.js +0 -105
- package/build/components/collapsible-sidebar/scss/index.scss +0 -2
- package/build/components/command/constants.d.ts +0 -3
- package/build/components/command/constants.js +0 -3
- package/build/components/command-palette/index.d.ts +0 -51
- package/build/components/command-palette/index.js +0 -319
- package/build/components/command-palette/scss/index.scss +0 -2
- package/build/components/composer/index.d.ts +0 -29
- package/build/components/composer/index.js +0 -79
- package/build/components/composer/scss/index.scss +0 -2
- package/build/components/composer/svg/arrow-up.svg +0 -1
- package/build/components/contribution-heatmap/index.d.ts +0 -30
- package/build/components/contribution-heatmap/index.js +0 -236
- package/build/components/contribution-heatmap/scss/index.scss +0 -2
- package/build/components/date-range-picker/index.d.ts +0 -28
- package/build/components/date-range-picker/index.js +0 -443
- package/build/components/date-range-picker/scss/index.scss +0 -2
- package/build/components/dynamic-island/index.d.ts +0 -23
- package/build/components/dynamic-island/index.js +0 -179
- package/build/components/dynamic-island/scss/index.scss +0 -2
- package/build/components/email-typo-fix/index.d.ts +0 -28
- package/build/components/email-typo-fix/index.js +0 -416
- package/build/components/email-typo-fix/scss/index.scss +0 -2
- package/build/components/expanding-card/index.d.ts +0 -26
- package/build/components/expanding-card/index.js +0 -265
- package/build/components/expanding-card/scss/index.scss +0 -2
- package/build/components/feature-spotlight/index.d.ts +0 -37
- package/build/components/feature-spotlight/index.js +0 -248
- package/build/components/feature-spotlight/scss/index.scss +0 -2
- package/build/components/file-tree/index.d.ts +0 -31
- package/build/components/file-tree/index.js +0 -208
- package/build/components/file-tree/scss/index.scss +0 -2
- package/build/components/filter-grid/index.d.ts +0 -23
- package/build/components/filter-grid/index.js +0 -240
- package/build/components/filter-grid/scss/index.scss +0 -2
- package/build/components/gradient-text/index.d.ts +0 -19
- package/build/components/gradient-text/index.js +0 -14
- package/build/components/gradient-text/scss/index.scss +0 -2
- package/build/components/header/index.d.ts +0 -41
- package/build/components/header/index.js +0 -281
- package/build/components/header/scss/index.scss +0 -2
- package/build/components/hover-card/index.d.ts +0 -47
- package/build/components/hover-card/index.js +0 -197
- package/build/components/hover-card/scss/index.scss +0 -2
- package/build/components/kanban-board/index.d.ts +0 -23
- package/build/components/kanban-board/index.js +0 -235
- package/build/components/kanban-board/scss/index.scss +0 -2
- package/build/components/lightbox/index.d.ts +0 -32
- package/build/components/lightbox/index.js +0 -396
- package/build/components/lightbox/scss/index.scss +0 -2
- package/build/components/lightbox/spring.d.ts +0 -16
- package/build/components/lightbox/spring.js +0 -71
- package/build/components/line-shadow-text/index.d.ts +0 -17
- package/build/components/line-shadow-text/index.js +0 -15
- package/build/components/line-shadow-text/scss/index.scss +0 -2
- package/build/components/log/index.d.ts +0 -20
- package/build/components/log/index.js +0 -125
- package/build/components/log/scss/index.scss +0 -2
- package/build/components/long-press/bind.d.ts +0 -21
- package/build/components/long-press/bind.js +0 -136
- package/build/components/long-press/button.d.ts +0 -21
- package/build/components/long-press/button.js +0 -25
- package/build/components/long-press/index.d.ts +0 -2181
- package/build/components/long-press/index.js +0 -4
- package/build/components/long-press/scss/index.scss +0 -2
- package/build/components/modal/index.d.ts +0 -24
- package/build/components/modal/index.js +0 -75
- package/build/components/modal/scss/index.scss +0 -2
- package/build/components/morphing-button/index.d.ts +0 -24
- package/build/components/morphing-button/index.js +0 -127
- package/build/components/morphing-button/scss/index.scss +0 -2
- package/build/components/morphing-nav/index.d.ts +0 -38
- package/build/components/morphing-nav/index.js +0 -342
- package/build/components/morphing-nav/scss/index.scss +0 -2
- package/build/components/multi-step-form/index.d.ts +0 -32
- package/build/components/multi-step-form/index.js +0 -305
- package/build/components/multi-step-form/scss/index.scss +0 -2
- package/build/components/navigation-menu/index.d.ts +0 -28
- package/build/components/navigation-menu/index.js +0 -419
- package/build/components/navigation-menu/scss/index.scss +0 -2
- package/build/components/nested-menu/index.d.ts +0 -27
- package/build/components/nested-menu/index.js +0 -374
- package/build/components/nested-menu/scss/index.scss +0 -2
- package/build/components/normalize/scss/index.scss +0 -3
- package/build/components/notification-bell/index.d.ts +0 -13
- package/build/components/notification-bell/index.js +0 -87
- package/build/components/notification-bell/scss/index.scss +0 -2
- package/build/components/onboarding-checklist/index.d.ts +0 -29
- package/build/components/onboarding-checklist/index.js +0 -196
- package/build/components/onboarding-checklist/scss/index.scss +0 -2
- package/build/components/page-dots/index.d.ts +0 -24
- package/build/components/page-dots/index.js +0 -116
- package/build/components/page-dots/scss/index.scss +0 -2
- package/build/components/pagination/svg/arrow-left.svg +0 -1
- package/build/components/pagination/svg/arrow-right.svg +0 -1
- package/build/components/password/constants.d.ts +0 -2
- package/build/components/password/constants.js +0 -2
- package/build/components/password/index.d.ts +0 -22
- package/build/components/password/index.js +0 -74
- package/build/components/password/scss/index.scss +0 -2
- package/build/components/plan-card/index.d.ts +0 -36
- package/build/components/plan-card/index.js +0 -257
- package/build/components/plan-card/scss/index.scss +0 -2
- package/build/components/radio-cards/index.d.ts +0 -36
- package/build/components/radio-cards/index.js +0 -106
- package/build/components/radio-cards/scss/index.scss +0 -2
- package/build/components/range-filter/index.d.ts +0 -20
- package/build/components/range-filter/index.js +0 -216
- package/build/components/range-filter/scss/index.scss +0 -2
- package/build/components/range-slider/index.d.ts +0 -30
- package/build/components/range-slider/index.js +0 -287
- package/build/components/range-slider/scss/index.scss +0 -2
- package/build/components/reading-progress/index.d.ts +0 -13
- package/build/components/reading-progress/index.js +0 -106
- package/build/components/reading-progress/scss/index.scss +0 -2
- package/build/components/scroll-fade/index.d.ts +0 -39
- package/build/components/scroll-fade/index.js +0 -104
- package/build/components/scroll-fade/scss/index.scss +0 -2
- package/build/components/scroll-spine/index.d.ts +0 -26
- package/build/components/scroll-spine/index.js +0 -204
- package/build/components/scroll-spine/scss/index.scss +0 -2
- package/build/components/scroll-velocity/index.d.ts +0 -22
- package/build/components/scroll-velocity/index.js +0 -160
- package/build/components/scroll-velocity/scss/index.scss +0 -2
- package/build/components/scrub-input/index.d.ts +0 -39
- package/build/components/scrub-input/index.js +0 -221
- package/build/components/scrub-input/scss/index.scss +0 -2
- package/build/components/select-menu/index.d.ts +0 -34
- package/build/components/select-menu/index.js +0 -329
- package/build/components/select-menu/scss/index.scss +0 -2
- package/build/components/selection-toolbar/index.d.ts +0 -36
- package/build/components/selection-toolbar/index.js +0 -442
- package/build/components/selection-toolbar/scss/index.scss +0 -2
- package/build/components/settings/index.d.ts +0 -43
- package/build/components/settings/index.js +0 -147
- package/build/components/settings/scss/index.scss +0 -2
- package/build/components/settings/svg/close.svg +0 -1
- package/build/components/settings/svg/saved.svg +0 -1
- package/build/components/share-button/index.d.ts +0 -29
- package/build/components/share-button/index.js +0 -191
- package/build/components/share-button/scss/index.scss +0 -2
- package/build/components/sheet/index.d.ts +0 -33
- package/build/components/sheet/index.js +0 -110
- package/build/components/sheet/scss/index.scss +0 -2
- package/build/components/shiny-text/index.d.ts +0 -17
- package/build/components/shiny-text/index.js +0 -14
- package/build/components/shiny-text/scss/index.scss +0 -2
- package/build/components/shortcut-sheet/index.d.ts +0 -48
- package/build/components/shortcut-sheet/index.js +0 -304
- package/build/components/shortcut-sheet/scss/index.scss +0 -2
- package/build/components/sidebar/index.d.ts +0 -13
- package/build/components/sidebar/index.js +0 -13
- package/build/components/sidebar/scss/index.scss +0 -2
- package/build/components/slider/index.d.ts +0 -25
- package/build/components/slider/index.js +0 -188
- package/build/components/slider/scss/index.scss +0 -2
- package/build/components/sliding-tabs/index.d.ts +0 -28
- package/build/components/sliding-tabs/index.js +0 -188
- package/build/components/sliding-tabs/scss/index.scss +0 -2
- package/build/components/snap-carousel/index.d.ts +0 -30
- package/build/components/snap-carousel/index.js +0 -233
- package/build/components/snap-carousel/scss/index.scss +0 -2
- package/build/components/sparkline/index.d.ts +0 -26
- package/build/components/sparkline/index.js +0 -173
- package/build/components/sparkline/scss/index.scss +0 -2
- package/build/components/stacked-drawer/index.d.ts +0 -24
- package/build/components/stacked-drawer/index.js +0 -211
- package/build/components/stacked-drawer/scss/index.scss +0 -2
- package/build/components/stat-counter/index.d.ts +0 -32
- package/build/components/stat-counter/index.js +0 -216
- package/build/components/stat-counter/scss/index.scss +0 -2
- package/build/components/status-picker/index.d.ts +0 -32
- package/build/components/status-picker/index.js +0 -198
- package/build/components/status-picker/scss/index.scss +0 -2
- package/build/components/sticky-stack/index.d.ts +0 -33
- package/build/components/sticky-stack/index.js +0 -184
- package/build/components/sticky-stack/scss/index.scss +0 -2
- package/build/components/story-progress/index.d.ts +0 -26
- package/build/components/story-progress/index.js +0 -196
- package/build/components/story-progress/scss/index.scss +0 -2
- package/build/components/swipe-deck/index.d.ts +0 -19
- package/build/components/swipe-deck/index.js +0 -300
- package/build/components/swipe-deck/scss/index.scss +0 -2
- package/build/components/tabs/scss/index.scss +0 -2
- package/build/components/tag-input/index.d.ts +0 -27
- package/build/components/tag-input/index.js +0 -309
- package/build/components/tag-input/scss/index.scss +0 -2
- package/build/components/task-list/index.d.ts +0 -28
- package/build/components/task-list/index.js +0 -107
- package/build/components/task-list/scss/index.scss +0 -2
- package/build/components/tasklist/constants.d.ts +0 -2
- package/build/components/tasklist/constants.js +0 -2
- package/build/components/text-animate/index.d.ts +0 -30
- package/build/components/text-animate/index.js +0 -97
- package/build/components/text-animate/scss/index.scss +0 -2
- package/build/components/text-progress/index.d.ts +0 -13
- package/build/components/text-progress/index.js +0 -114
- package/build/components/text-progress/scss/index.scss +0 -2
- package/build/components/thumbnail/scss/index.scss +0 -2
- package/build/components/toast/manager.d.ts +0 -64
- package/build/components/toast/manager.js +0 -221
- package/build/components/toast/svg/check.svg +0 -1
- package/build/components/toast/svg/chevron.svg +0 -1
- package/build/components/toast/svg/close.svg +0 -1
- package/build/components/toast/svg/copy.svg +0 -1
- package/build/components/toast/svg/info.svg +0 -1
- package/build/components/tooltip/context.d.ts +0 -22
- package/build/components/tooltip/context.js +0 -66
- package/build/components/tooltip/group.d.ts +0 -29
- package/build/components/tooltip/group.js +0 -128
- package/build/components/tooltip/options.d.ts +0 -7
- package/build/components/tooltip/options.js +0 -21
- package/build/components/tree/index.d.ts +0 -28
- package/build/components/tree/index.js +0 -182
- package/build/components/tree/scss/index.scss +0 -2
- package/build/components/tree-view/index.d.ts +0 -25
- package/build/components/tree-view/index.js +0 -205
- package/build/components/tree-view/scss/index.scss +0 -2
- package/build/components/typewriter-retype/index.d.ts +0 -8
- package/build/components/typewriter-retype/index.js +0 -122
- package/build/components/typewriter-retype/scss/index.scss +0 -2
- package/build/components/undo-toast/index.d.ts +0 -30
- package/build/components/undo-toast/index.js +0 -262
- package/build/components/undo-toast/scss/index.scss +0 -2
- package/build/components/value-flash/index.d.ts +0 -12
- package/build/components/value-flash/index.js +0 -106
- package/build/components/value-flash/scss/index.scss +0 -2
- package/build/components/waitlist-join/index.d.ts +0 -34
- package/build/components/waitlist-join/index.js +0 -402
- package/build/components/waitlist-join/scss/index.scss +0 -2
- package/build/components/web-search/brands.d.ts +0 -121
- package/build/components/web-search/brands.js +0 -138
- package/build/components/web-search/index.d.ts +0 -31
- package/build/components/web-search/index.js +0 -245
- package/build/components/web-search/scss/index.scss +0 -2
- package/build/components/word-rotator/index.d.ts +0 -14
- package/build/components/word-rotator/index.js +0 -196
- package/build/components/word-rotator/scss/index.scss +0 -2
- package/build/css-utilities/absolute/scss/index.scss +0 -2
- package/build/css-utilities/disabled/scss/index.scss +0 -2
- package/build/css-utilities/hidden/scss/index.scss +0 -2
- package/build/css-utilities/not-allowed/scss/index.scss +0 -2
- package/build/css-utilities/tabular/scss/index.scss +0 -2
- package/lib.scss +0 -2
- package/src/components/accordion/more.ts +0 -97
- package/src/components/anchor/scss/index.scss +0 -56
- package/src/components/anchor/scss/variables.scss +0 -4
- package/src/components/announcement-banner/index.ts +0 -81
- package/src/components/announcement-banner/scss/index.scss +0 -183
- package/src/components/announcement-banner/scss/variables.scss +0 -18
- package/src/components/autosave/index.ts +0 -113
- package/src/components/autosave/scss/index.scss +0 -155
- package/src/components/autosave/scss/variables.scss +0 -22
- package/src/components/back-to-top/index.ts +0 -293
- package/src/components/back-to-top/scss/index.scss +0 -111
- package/src/components/back-to-top/scss/variables.scss +0 -30
- package/src/components/bento-grid/index.ts +0 -63
- package/src/components/bento-grid/scss/index.scss +0 -175
- package/src/components/bento-grid/scss/variables.scss +0 -19
- package/src/components/bento-grid-spotlight/index.ts +0 -133
- package/src/components/bento-grid-spotlight/scss/index.scss +0 -226
- package/src/components/bento-grid-spotlight/scss/variables.scss +0 -36
- package/src/components/bookmark-button/index.ts +0 -174
- package/src/components/bookmark-button/scss/index.scss +0 -162
- package/src/components/bookmark-button/scss/variables.scss +0 -28
- package/src/components/breadcrumb/ellipsis.ts +0 -17
- package/src/components/breadcrumb/item.ts +0 -10
- package/src/components/breadcrumb/link.ts +0 -21
- package/src/components/breadcrumb/list.ts +0 -10
- package/src/components/breadcrumb/menu.ts +0 -20
- package/src/components/breadcrumb/page.ts +0 -16
- package/src/components/breadcrumb/separator.ts +0 -17
- package/src/components/breadcrumb/svg/caret.svg +0 -1
- package/src/components/breadcrumb/svg/chevron.svg +0 -1
- package/src/components/breadcrumb/svg/more.svg +0 -1
- package/src/components/breadcrumbs/index.ts +0 -479
- package/src/components/breadcrumbs/scss/index.scss +0 -235
- package/src/components/breadcrumbs/scss/variables.scss +0 -30
- package/src/components/button/svg/plus.svg +0 -1
- package/src/components/card-input/index.ts +0 -594
- package/src/components/card-input/scss/index.scss +0 -512
- package/src/components/card-input/scss/variables.scss +0 -29
- package/src/components/carousel-3d/index.ts +0 -405
- package/src/components/carousel-3d/scss/index.scss +0 -155
- package/src/components/carousel-3d/scss/variables.scss +0 -25
- package/src/components/checkbox/svg/check.svg +0 -1
- package/src/components/checkbox-group/index.ts +0 -171
- package/src/components/checkbox-group/scss/index.scss +0 -229
- package/src/components/checkbox-group/scss/variables.scss +0 -28
- package/src/components/code-morph/index.ts +0 -459
- package/src/components/code-morph/scss/index.scss +0 -384
- package/src/components/code-morph/scss/variables.scss +0 -33
- package/src/components/collapsible-sidebar/index.ts +0 -193
- package/src/components/collapsible-sidebar/scss/index.scss +0 -220
- package/src/components/collapsible-sidebar/scss/variables.scss +0 -42
- package/src/components/command/constants.ts +0 -6
- package/src/components/command-palette/index.ts +0 -456
- package/src/components/command-palette/scss/index.scss +0 -235
- package/src/components/command-palette/scss/variables.scss +0 -42
- package/src/components/composer/index.ts +0 -117
- package/src/components/composer/scss/index.scss +0 -105
- package/src/components/composer/scss/variables.scss +0 -82
- package/src/components/composer/svg/arrow-up.svg +0 -1
- package/src/components/contribution-heatmap/index.ts +0 -348
- package/src/components/contribution-heatmap/scss/index.scss +0 -151
- package/src/components/contribution-heatmap/scss/variables.scss +0 -39
- package/src/components/date-range-picker/index.ts +0 -641
- package/src/components/date-range-picker/scss/index.scss +0 -380
- package/src/components/date-range-picker/scss/variables.scss +0 -22
- package/src/components/dynamic-island/index.ts +0 -161
- package/src/components/dynamic-island/scss/index.scss +0 -278
- package/src/components/dynamic-island/scss/variables.scss +0 -39
- package/src/components/email-typo-fix/index.ts +0 -615
- package/src/components/email-typo-fix/scss/index.scss +0 -213
- package/src/components/email-typo-fix/scss/variables.scss +0 -18
- package/src/components/expanding-card/index.ts +0 -377
- package/src/components/expanding-card/scss/index.scss +0 -232
- package/src/components/expanding-card/scss/variables.scss +0 -22
- package/src/components/feature-spotlight/index.ts +0 -437
- package/src/components/feature-spotlight/scss/index.scss +0 -254
- package/src/components/feature-spotlight/scss/variables.scss +0 -36
- package/src/components/file-tree/index.ts +0 -319
- package/src/components/file-tree/scss/index.scss +0 -138
- package/src/components/file-tree/scss/variables.scss +0 -18
- package/src/components/filter-grid/index.ts +0 -344
- package/src/components/filter-grid/scss/index.scss +0 -142
- package/src/components/filter-grid/scss/variables.scss +0 -41
- package/src/components/gradient-text/index.ts +0 -26
- package/src/components/gradient-text/scss/index.scss +0 -20
- package/src/components/gradient-text/scss/variables.scss +0 -6
- package/src/components/header/index.ts +0 -318
- package/src/components/header/scss/index.scss +0 -508
- package/src/components/header/scss/variables.scss +0 -21
- package/src/components/hover-card/index.ts +0 -319
- package/src/components/hover-card/scss/index.scss +0 -117
- package/src/components/hover-card/scss/variables.scss +0 -25
- package/src/components/kanban-board/index.ts +0 -326
- package/src/components/kanban-board/scss/index.scss +0 -126
- package/src/components/kanban-board/scss/variables.scss +0 -39
- package/src/components/lightbox/index.ts +0 -594
- package/src/components/lightbox/scss/index.scss +0 -194
- package/src/components/lightbox/scss/variables.scss +0 -24
- package/src/components/lightbox/spring.ts +0 -118
- package/src/components/line-shadow-text/index.ts +0 -24
- package/src/components/line-shadow-text/scss/index.scss +0 -45
- package/src/components/line-shadow-text/scss/variables.scss +0 -6
- package/src/components/log/index.ts +0 -156
- package/src/components/log/scss/index.scss +0 -269
- package/src/components/log/scss/variables.scss +0 -19
- package/src/components/long-press/bind.ts +0 -159
- package/src/components/long-press/button.ts +0 -46
- package/src/components/long-press/index.ts +0 -6
- package/src/components/long-press/scss/index.scss +0 -60
- package/src/components/long-press/scss/variables.scss +0 -9
- package/src/components/modal/index.ts +0 -91
- package/src/components/modal/scss/index.scss +0 -81
- package/src/components/modal/scss/variables.scss +0 -42
- package/src/components/morphing-button/index.ts +0 -188
- package/src/components/morphing-button/scss/index.scss +0 -154
- package/src/components/morphing-button/scss/variables.scss +0 -21
- package/src/components/morphing-nav/index.ts +0 -496
- package/src/components/morphing-nav/scss/index.scss +0 -284
- package/src/components/morphing-nav/scss/variables.scss +0 -36
- package/src/components/multi-step-form/index.ts +0 -368
- package/src/components/multi-step-form/scss/index.scss +0 -397
- package/src/components/multi-step-form/scss/variables.scss +0 -22
- package/src/components/navigation-menu/index.ts +0 -608
- package/src/components/navigation-menu/scss/index.scss +0 -213
- package/src/components/navigation-menu/scss/variables.scss +0 -24
- package/src/components/nested-menu/index.ts +0 -446
- package/src/components/nested-menu/scss/index.scss +0 -219
- package/src/components/nested-menu/scss/variables.scss +0 -26
- package/src/components/normalize/scss/index.scss +0 -35
- package/src/components/notification-bell/index.ts +0 -102
- package/src/components/notification-bell/scss/index.scss +0 -132
- package/src/components/notification-bell/scss/variables.scss +0 -41
- package/src/components/notification-bell/svg/bell.svg +0 -1
- package/src/components/onboarding-checklist/index.ts +0 -252
- package/src/components/onboarding-checklist/scss/index.scss +0 -414
- package/src/components/onboarding-checklist/scss/variables.scss +0 -17
- package/src/components/page-dots/index.ts +0 -137
- package/src/components/page-dots/scss/index.scss +0 -95
- package/src/components/page-dots/scss/variables.scss +0 -26
- package/src/components/pagination/svg/arrow-left.svg +0 -1
- package/src/components/pagination/svg/arrow-right.svg +0 -1
- package/src/components/password/constants.ts +0 -4
- package/src/components/password/index.ts +0 -97
- package/src/components/password/scss/index.scss +0 -188
- package/src/components/password/scss/variables.scss +0 -50
- package/src/components/plan-card/index.ts +0 -404
- package/src/components/plan-card/scss/index.scss +0 -513
- package/src/components/plan-card/scss/variables.scss +0 -33
- package/src/components/radio-cards/index.ts +0 -145
- package/src/components/radio-cards/scss/index.scss +0 -232
- package/src/components/radio-cards/scss/variables.scss +0 -32
- package/src/components/range-filter/index.ts +0 -295
- package/src/components/range-filter/scss/index.scss +0 -257
- package/src/components/range-filter/scss/variables.scss +0 -20
- package/src/components/range-slider/index.ts +0 -277
- package/src/components/range-slider/scss/index.scss +0 -234
- package/src/components/range-slider/scss/variables.scss +0 -26
- package/src/components/reading-progress/index.ts +0 -160
- package/src/components/reading-progress/scss/index.scss +0 -115
- package/src/components/reading-progress/scss/variables.scss +0 -31
- package/src/components/scroll-fade/index.ts +0 -131
- package/src/components/scroll-fade/scss/index.scss +0 -104
- package/src/components/scroll-fade/scss/variables.scss +0 -10
- package/src/components/scroll-spine/index.ts +0 -355
- package/src/components/scroll-spine/scss/index.scss +0 -209
- package/src/components/scroll-spine/scss/variables.scss +0 -22
- package/src/components/scroll-velocity/index.ts +0 -269
- package/src/components/scroll-velocity/scss/index.scss +0 -29
- package/src/components/scroll-velocity/scss/variables.scss +0 -3
- package/src/components/scrub-input/index.ts +0 -339
- package/src/components/scrub-input/scss/index.scss +0 -105
- package/src/components/scrub-input/scss/variables.scss +0 -23
- package/src/components/select-menu/index.ts +0 -499
- package/src/components/select-menu/scss/index.scss +0 -192
- package/src/components/select-menu/scss/variables.scss +0 -41
- package/src/components/selection-toolbar/index.ts +0 -510
- package/src/components/selection-toolbar/scss/index.scss +0 -221
- package/src/components/selection-toolbar/scss/variables.scss +0 -24
- package/src/components/settings/index.ts +0 -184
- package/src/components/settings/scss/index.scss +0 -327
- package/src/components/settings/scss/variables.scss +0 -50
- package/src/components/settings/svg/close.svg +0 -1
- package/src/components/settings/svg/saved.svg +0 -1
- package/src/components/share-button/index.ts +0 -328
- package/src/components/share-button/scss/index.scss +0 -191
- package/src/components/share-button/scss/variables.scss +0 -24
- package/src/components/sheet/index.ts +0 -170
- package/src/components/sheet/scss/index.scss +0 -65
- package/src/components/sheet/scss/variables.scss +0 -27
- package/src/components/shiny-text/index.ts +0 -19
- package/src/components/shiny-text/scss/index.scss +0 -32
- package/src/components/shiny-text/scss/variables.scss +0 -7
- package/src/components/shortcut-sheet/index.ts +0 -454
- package/src/components/shortcut-sheet/scss/index.scss +0 -306
- package/src/components/shortcut-sheet/scss/variables.scss +0 -36
- package/src/components/sidebar/index.ts +0 -13
- package/src/components/sidebar/scss/index.scss +0 -49
- package/src/components/sidebar/scss/variables.scss +0 -20
- package/src/components/slider/index.ts +0 -232
- package/src/components/slider/scss/index.scss +0 -182
- package/src/components/slider/scss/variables.scss +0 -15
- package/src/components/sliding-tabs/index.ts +0 -291
- package/src/components/sliding-tabs/scss/index.scss +0 -174
- package/src/components/sliding-tabs/scss/variables.scss +0 -34
- package/src/components/snap-carousel/index.ts +0 -324
- package/src/components/snap-carousel/scss/index.scss +0 -174
- package/src/components/snap-carousel/scss/variables.scss +0 -31
- package/src/components/sparkline/index.ts +0 -234
- package/src/components/sparkline/scss/index.scss +0 -169
- package/src/components/sparkline/scss/variables.scss +0 -30
- package/src/components/stacked-drawer/index.ts +0 -272
- package/src/components/stacked-drawer/scss/index.scss +0 -122
- package/src/components/stacked-drawer/scss/variables.scss +0 -20
- package/src/components/stat-counter/index.ts +0 -337
- package/src/components/stat-counter/scss/index.scss +0 -246
- package/src/components/stat-counter/scss/variables.scss +0 -24
- package/src/components/status-picker/index.ts +0 -323
- package/src/components/status-picker/scss/index.scss +0 -290
- package/src/components/status-picker/scss/variables.scss +0 -29
- package/src/components/sticky-stack/index.ts +0 -313
- package/src/components/sticky-stack/scss/index.scss +0 -167
- package/src/components/sticky-stack/scss/variables.scss +0 -50
- package/src/components/story-progress/index.ts +0 -247
- package/src/components/story-progress/scss/index.scss +0 -205
- package/src/components/story-progress/scss/variables.scss +0 -21
- package/src/components/swipe-deck/index.ts +0 -438
- package/src/components/swipe-deck/scss/index.scss +0 -275
- package/src/components/swipe-deck/scss/variables.scss +0 -24
- package/src/components/tabs/scss/index.scss +0 -62
- package/src/components/tabs/scss/variables.scss +0 -4
- package/src/components/tag-input/index.ts +0 -415
- package/src/components/tag-input/scss/index.scss +0 -186
- package/src/components/tag-input/scss/variables.scss +0 -30
- package/src/components/task-list/index.ts +0 -137
- package/src/components/task-list/scss/index.scss +0 -171
- package/src/components/task-list/scss/variables.scss +0 -8
- package/src/components/tasklist/constants.ts +0 -4
- package/src/components/text/scss/variables.scss +0 -9
- package/src/components/text-animate/index.ts +0 -142
- package/src/components/text-animate/scss/index.scss +0 -106
- package/src/components/text-animate/scss/variables.scss +0 -17
- package/src/components/text-progress/index.ts +0 -170
- package/src/components/text-progress/scss/index.scss +0 -148
- package/src/components/text-progress/scss/variables.scss +0 -26
- package/src/components/thumbnail/scss/index.scss +0 -62
- package/src/components/thumbnail/scss/variables.scss +0 -19
- package/src/components/toast/manager.ts +0 -342
- package/src/components/toast/svg/check.svg +0 -1
- package/src/components/toast/svg/chevron.svg +0 -1
- package/src/components/toast/svg/close.svg +0 -1
- package/src/components/toast/svg/copy.svg +0 -1
- package/src/components/toast/svg/error.svg +0 -1
- package/src/components/toast/svg/info.svg +0 -1
- package/src/components/toast/svg/loading.svg +0 -1
- package/src/components/toast/svg/success.svg +0 -1
- package/src/components/toast/svg/warning.svg +0 -1
- package/src/components/tooltip/context.ts +0 -96
- package/src/components/tooltip/group.ts +0 -162
- package/src/components/tooltip/options.ts +0 -33
- package/src/components/tooltip/scss/_group.scss +0 -164
- package/src/components/tree/index.ts +0 -259
- package/src/components/tree/scss/index.scss +0 -105
- package/src/components/tree/scss/variables.scss +0 -48
- package/src/components/tree-view/index.ts +0 -285
- package/src/components/tree-view/scss/index.scss +0 -169
- package/src/components/tree-view/scss/variables.scss +0 -18
- package/src/components/typewriter-retype/index.ts +0 -164
- package/src/components/typewriter-retype/scss/index.scss +0 -142
- package/src/components/typewriter-retype/scss/variables.scss +0 -46
- package/src/components/undo-toast/index.ts +0 -359
- package/src/components/undo-toast/scss/index.scss +0 -216
- package/src/components/undo-toast/scss/variables.scss +0 -20
- package/src/components/value-flash/index.ts +0 -121
- package/src/components/value-flash/scss/index.scss +0 -193
- package/src/components/value-flash/scss/variables.scss +0 -47
- package/src/components/waitlist-join/index.ts +0 -545
- package/src/components/waitlist-join/scss/index.scss +0 -401
- package/src/components/waitlist-join/scss/variables.scss +0 -23
- package/src/components/web-search/brands.ts +0 -155
- package/src/components/web-search/index.ts +0 -316
- package/src/components/web-search/scss/index.scss +0 -270
- package/src/components/web-search/scss/variables.scss +0 -8
- package/src/components/word-rotator/index.ts +0 -296
- package/src/components/word-rotator/scss/index.scss +0 -33
- package/src/components/word-rotator/scss/variables.scss +0 -7
- package/src/css-utilities/absolute/scss/index.scss +0 -36
- package/src/css-utilities/disabled/scss/index.scss +0 -4
- package/src/css-utilities/hidden/scss/index.scss +0 -28
- package/src/css-utilities/not-allowed/scss/index.scss +0 -7
- package/src/css-utilities/tabular/scss/index.scss +0 -3
- package/src/lib/index.scss +0 -7
- package/src/lib/scss/breakpoint.scss +0 -41
- package/src/lib/scss/color.scss +0 -1
- package/src/lib/scss/list.scss +0 -77
- package/src/lib/scss/string.scss +0 -34
- package/src/tokens/index.scss +0 -9
- package/storage/psd/Color Palette.psd +0 -0
- /package/src/{lib → shared}/scss/css-variables.scss +0 -0
- /package/src/{lib → shared}/scss/map.scss +0 -0
- /package/src/{lib → shared}/scss/position.scss +0 -0
- /package/{build/components/notification-bell → storage}/svg/bell.svg +0 -0
- /package/{build/components/checkbox → storage}/svg/check.svg +0 -0
- /package/{build/components/toast/svg/error.svg → storage/svg/circle-alert.svg} +0 -0
- /package/{build/components/toast/svg/success.svg → storage/svg/circle-check.svg} +0 -0
- /package/{build/components/toast/svg/loading.svg → storage/svg/spinner.svg} +0 -0
- /package/{build/components/toast → storage}/svg/warning.svg +0 -0
|
@@ -0,0 +1,3438 @@
|
|
|
1
|
+
import { effect, flush, peek, reactive, ReactiveArray, read, signal, untrack, write, type Signal } from '@esportsplus/reactivity';
|
|
2
|
+
import { html, type Attributes, type Renderable } from '@esportsplus/template';
|
|
3
|
+
import { write as writeText } from '~/components/clipboard';
|
|
4
|
+
import highlight from '~/components/highlight';
|
|
5
|
+
import icon from '~/components/icon';
|
|
6
|
+
import tooltip from '~/components/tooltip';
|
|
7
|
+
import fuzzy from '~/shared/fuzzy';
|
|
8
|
+
import chevron from '@esportsplus/ui/svg/chevron-right.svg';
|
|
9
|
+
import symlink from '@esportsplus/ui/svg/corner-down-right.svg';
|
|
10
|
+
import lock from '@esportsplus/ui/svg/lock.svg';
|
|
11
|
+
import Clipboard from './clipboard';
|
|
12
|
+
import segments, { ancestors, caption, holds, lineage, same, SEGMENT, sole, type Segment } from './compact';
|
|
13
|
+
import Decorations, { merge, type Badge, type Decoration, type Status, type Tone } from './decorations';
|
|
14
|
+
import draggable, { type Drag, type Drop } from './drag';
|
|
15
|
+
import Editor, { field, resolve, type Draft, type Kind, type Result } from './edit';
|
|
16
|
+
import filter, { recheck } from './filter';
|
|
17
|
+
import Finder, { type Mode } from './find';
|
|
18
|
+
import glob from './glob';
|
|
19
|
+
import glyph, { renderer } from './glyph';
|
|
20
|
+
import History, { type Entry as HistoryEntry, type Operation, type Options as HistoryOptions, type Place, type Step } from './history';
|
|
21
|
+
import { resolver as createFileTreeIconResolver, resolve as resolveFileTreeIcon, type Name as IconName, type Options as IconOptions, type Resolved as ResolvedIcon } from './icons';
|
|
22
|
+
import { enclosing, flagged, order, seek, type Jump } from './jump';
|
|
23
|
+
import Loader, { placeholder, type Load, type Notice } from './lazy';
|
|
24
|
+
import Elements, { type Change, type Entry } from './model';
|
|
25
|
+
import Selection from './selection';
|
|
26
|
+
import command, { extending, type Command } from './shortcuts';
|
|
27
|
+
import comparator, { type Case, type Options, type Order, type Sort } from './sort';
|
|
28
|
+
import spoken from './spoken';
|
|
29
|
+
import stick from './sticky';
|
|
30
|
+
import type { Export, ImportEntry } from './transfer';
|
|
31
|
+
import '~/components/button/scss/index.scss';
|
|
32
|
+
import './scss/index.scss';
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
type A = Attributes & {
|
|
36
|
+
// A chain of folders each holding only one folder shows as one row, 'src/main/java', as VS Code's compact
|
|
37
|
+
// folders do; on unless false.
|
|
38
|
+
compact?: boolean;
|
|
39
|
+
// Called once with the tree's commands, for toolbars and shortcuts outside it.
|
|
40
|
+
controller?: (controller: Controller) => void;
|
|
41
|
+
// A name typed into a new item's input. 'a/b.ts' arrives as ['a', 'b.ts'], the folders to create and then the
|
|
42
|
+
// item, under the deepest folder along the path that already exists.
|
|
43
|
+
create?: (parent: Element | null, parts: string[], kind: Kind) => Result;
|
|
44
|
+
// One store per provider, merged by id.
|
|
45
|
+
decorations?: Decorations | Decorations[];
|
|
46
|
+
display?: Display;
|
|
47
|
+
// Rows dragged onto a folder, or onto a file for its folder; the tree reports the drop and moves nothing itself.
|
|
48
|
+
drag?: Drag<Element>;
|
|
49
|
+
editor?: Editor;
|
|
50
|
+
// A store to change the tree live through; a plain array is fixed.
|
|
51
|
+
elements: Element[] | Elements;
|
|
52
|
+
// Shown in place of the rows when there are none, like a message with a button to open a folder.
|
|
53
|
+
empty?: () => Renderable<unknown>;
|
|
54
|
+
// Globs never shown, like VS Code's 'files.exclude': '**/node_modules', 'dist/**', '**/*.{log,tmp}'.
|
|
55
|
+
exclude?: string[];
|
|
56
|
+
// Whether a click on a folder's name opens it, or only a double click does; its chevron always opens on one.
|
|
57
|
+
expand?: 'click' | 'dblclick';
|
|
58
|
+
// Folder ids open on mount; the selected item's folders open as well, unless 'reveal' is off.
|
|
59
|
+
expanded?: string[];
|
|
60
|
+
// What a row dragged out to the desktop or another app leaves there, null for nothing; given, a mouse drags rows
|
|
61
|
+
// the browser's own way, moves within the tree included.
|
|
62
|
+
export?: (element: Element) => Export | null;
|
|
63
|
+
// Ctrl/Cmd+Alt+F opens a find bar over the tree, in this mode until toggled: 'highlight' marks the matches among
|
|
64
|
+
// every row, 'filter' leaves only them and the folders above them, opened while it lasts.
|
|
65
|
+
find?: Mode | 'off';
|
|
66
|
+
// A fuzzy query whose matched characters are marked in each name, like quick open's; a getter keeps them in step.
|
|
67
|
+
highlight?: string | (() => string);
|
|
68
|
+
// Ctrl/Cmd+Z undoes its last entry and Ctrl/Cmd+Shift+Z or Ctrl+Y redoes one; whatever an undo or redo brings
|
|
69
|
+
// back, moves or renames, however it was started, is selected and revealed.
|
|
70
|
+
history?: History;
|
|
71
|
+
// Draws every row's icon in place of the built-in ones; what it returns is drawn as is, nothing for undefined.
|
|
72
|
+
// 'fileTreeIcon' draws the built-in one, for the rows it leaves be.
|
|
73
|
+
icon?: (element: Element, open: boolean) => Renderable<unknown>;
|
|
74
|
+
// Rules mapping names, name fragments, extensions and folders to built-in icons or any sprite, on top of the
|
|
75
|
+
// built-in set; false draws no icons.
|
|
76
|
+
icons?: IconOptions | false;
|
|
77
|
+
// Draws a guide line down each open folder.
|
|
78
|
+
indicator?: Indicator;
|
|
79
|
+
label?: string;
|
|
80
|
+
// Fetches the children of a folder given without any, the first time it opens.
|
|
81
|
+
load?: Load;
|
|
82
|
+
// Right-click, Shift+F10 or the menu key: the caller shows its own menu at 'position'. No elements means the
|
|
83
|
+
// tree's background; 'root' that it opened on a root's header, for commands like removing it from the workspace.
|
|
84
|
+
menu?: (elements: Element[], position: { x: number; y: number }, root: boolean) => void;
|
|
85
|
+
// A file was clicked or entered: the viewer should show it.
|
|
86
|
+
open?: (element: Element, open: Open) => void;
|
|
87
|
+
operations?: Operations;
|
|
88
|
+
// What Shift+Alt+C copies, and Ctrl+Shift+Alt+C as 'relative'; the id by default.
|
|
89
|
+
path?: (element: Element, relative: boolean) => string;
|
|
90
|
+
// Moving the cursor onto a file opens it as a preview once the cursor rests, like JetBrains' autoscroll to source.
|
|
91
|
+
// 'immediate' follows keyboard/typeahead navigation without the resting-cursor delay.
|
|
92
|
+
preview?: boolean | 'immediate';
|
|
93
|
+
// F2 renames the focused item.
|
|
94
|
+
rename?: (element: Element, name: string) => Result;
|
|
95
|
+
// Selection written from outside: 'on' opens the folders down to it and scrolls to it, 'select' opens them
|
|
96
|
+
// without scrolling, 'off' only moves the highlight.
|
|
97
|
+
reveal?: 'off' | 'on' | 'select';
|
|
98
|
+
// The top-level folders are roots, as in a VS Code multi-root workspace: each heads its own tree, in the order
|
|
99
|
+
// given, open unless a snapshot says otherwise. Patterns match paths within each root. The tree never renames,
|
|
100
|
+
// moves or deletes a root; adding and removing them is the store's.
|
|
101
|
+
roots?: boolean;
|
|
102
|
+
// Tints rows by the first scope matching them, or else by their folder's.
|
|
103
|
+
scopes?: Scope[];
|
|
104
|
+
// The query the rows show changed from within the tree, by typing in it, its find bar or Escape; not called for
|
|
105
|
+
// 'Controller.search', so a search field outside can follow it.
|
|
106
|
+
searched?: (query: string) => void;
|
|
107
|
+
select?: (element: Element) => void;
|
|
108
|
+
selected?: string;
|
|
109
|
+
// Alt+N starts a new file and Alt+Shift+N a new folder.
|
|
110
|
+
shortcuts?: boolean;
|
|
111
|
+
// Seeds the open folders and scroll position on mount, in place of 'expanded', and receives every change, so it
|
|
112
|
+
// can be stored and handed back later.
|
|
113
|
+
snapshot?: Snapshot;
|
|
114
|
+
sort?: Sort;
|
|
115
|
+
state?: State;
|
|
116
|
+
// Pins the folders the top rows sit in as the list scrolls, this many deep at most; 'true' is five.
|
|
117
|
+
sticky?: boolean | number;
|
|
118
|
+
// Adds a corner button that opens every folder, or closes them all when any are open.
|
|
119
|
+
toggle?: boolean;
|
|
120
|
+
// Hovering a row shows its full path, with its size and modified date when given.
|
|
121
|
+
tooltip?: boolean;
|
|
122
|
+
// What typing a letter in the tree does: jump to the next name starting with what's typed, or open the find bar
|
|
123
|
+
// with it, as VS Code's 'workbench.list.typeNavigationMode' chooses.
|
|
124
|
+
typing?: 'find' | 'typeahead';
|
|
125
|
+
// Runs once a typed name passes the built-in checks; a message returned blocks the commit and shows under the input.
|
|
126
|
+
validate?: (name: string, parent: Element | null, element: Element | null) => string | void;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
type Align = 'center' | 'end' | 'start';
|
|
130
|
+
|
|
131
|
+
type Controller = {
|
|
132
|
+
collapseAll: () => void;
|
|
133
|
+
expandAll: () => void;
|
|
134
|
+
// Open the find bar, with 'query' in place of what it last held when given.
|
|
135
|
+
find: (query?: string) => void;
|
|
136
|
+
// Move the cursor to the next or previous file git reports changed, or with errors or warnings, in the order the
|
|
137
|
+
// rows show, wrapping round, and return it; null when there's none. Folders open on the way, and lazy ones load
|
|
138
|
+
// when ids are paths and a decorated id lies inside one.
|
|
139
|
+
next: (kind: Jump) => Promise<Element | null>;
|
|
140
|
+
previous: (kind: Jump) => Promise<Element | null>;
|
|
141
|
+
// Apply a search field's query outside the tree, without opening or focusing the find bar; 'searched' reports
|
|
142
|
+
// what the tree does to it.
|
|
143
|
+
search: (query: string) => void;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
type Display = {
|
|
147
|
+
// Files git reports deleted are gone from disk, so they're hidden unless asked for, then shown struck through.
|
|
148
|
+
deleted?: boolean;
|
|
149
|
+
// Names starting with a dot; shown unless false.
|
|
150
|
+
dotfiles?: boolean;
|
|
151
|
+
// Files git ignores, dimmed; hidden when false, and shown again as soon as git stops ignoring them.
|
|
152
|
+
ignored?: boolean;
|
|
153
|
+
// Lines added and removed, from each decoration's 'additions' and 'deletions', ahead of the status letter; a
|
|
154
|
+
// folder shows the totals of what's inside it.
|
|
155
|
+
stats?: boolean;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
type Element = {
|
|
159
|
+
children?: Element[];
|
|
160
|
+
id: string;
|
|
161
|
+
// Read by the 'modified' sort order and the path tooltip.
|
|
162
|
+
modified?: Date | number;
|
|
163
|
+
name: string;
|
|
164
|
+
readonly?: boolean;
|
|
165
|
+
// Shown but inert, like a locked file.
|
|
166
|
+
selectable?: boolean;
|
|
167
|
+
// In bytes.
|
|
168
|
+
size?: number;
|
|
169
|
+
symlink?: boolean;
|
|
170
|
+
type?: 'file' | 'folder';
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// A row below a folder that folds, where it sat before the list swapped rows; drawn while the edge travels.
|
|
174
|
+
type Ghost = {
|
|
175
|
+
left: number;
|
|
176
|
+
row: Row;
|
|
177
|
+
top: number;
|
|
178
|
+
width: number;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Where guides show: always, only while the pointer is over the tree, or never.
|
|
182
|
+
type Indicator = 'always' | 'hover' | 'never';
|
|
183
|
+
|
|
184
|
+
type Layout = Pick<Row, 'depth' | 'element' | 'gaps' | 'parent' | 'position' | 'scope' | 'segments' | 'size'>;
|
|
185
|
+
|
|
186
|
+
type Mark = {
|
|
187
|
+
// What the row shows and says, flattened, so a refresh writes only when it changed.
|
|
188
|
+
key: string;
|
|
189
|
+
label: string;
|
|
190
|
+
parts: Part[];
|
|
191
|
+
tone: Tone | '';
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// How a file asked to be opened; the viewer decides what that means for its tabs.
|
|
195
|
+
type Open = {
|
|
196
|
+
// A single click or a resting cursor previews, replacing the last preview; a double or middle click or Enter pins.
|
|
197
|
+
mode: 'pinned' | 'preview';
|
|
198
|
+
// Alt+click or Ctrl+Enter: beside the current file rather than in its place.
|
|
199
|
+
side: boolean;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Reported with the elements they act on; the tree itself never touches a file.
|
|
203
|
+
type Operations = {
|
|
204
|
+
// Asked before a delete; false cancels it.
|
|
205
|
+
confirm?: (elements: Element[], permanent: boolean) => boolean | Promise<boolean>;
|
|
206
|
+
copy?: (elements: Element[]) => void;
|
|
207
|
+
cut?: (elements: Element[]) => void;
|
|
208
|
+
// Delete is meant for the trash, Shift+Delete for good.
|
|
209
|
+
delete?: (elements: Element[], permanent: boolean) => void;
|
|
210
|
+
duplicate?: (elements: Element[]) => void;
|
|
211
|
+
// Files and folders dropped in from outside the browser onto 'target', a folder or null for the top level, every
|
|
212
|
+
// folder walked through; without it such drops are refused.
|
|
213
|
+
import?: (target: Element | null, entries: readonly ImportEntry[]) => void | Promise<void>;
|
|
214
|
+
// Into the focused folder, or the focused file's folder; null is the top level. Cut items stay dimmed until the
|
|
215
|
+
// returned promise settles.
|
|
216
|
+
paste?: (elements: Element[], target: Element | null, cut: boolean) => void | Promise<void>;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Each part of the badge is colored on its own, so a modified file's diff stats read green and red beside its yellow M.
|
|
220
|
+
type Part = {
|
|
221
|
+
color?: string;
|
|
222
|
+
staged?: boolean;
|
|
223
|
+
text: string;
|
|
224
|
+
title?: string;
|
|
225
|
+
tone?: Tone | 'additions' | 'deletions' | 'open' | 'submodule' | 'unsaved';
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
type Rendered = HTMLElement & { [ROW]?: Row };
|
|
229
|
+
|
|
230
|
+
type Row = {
|
|
231
|
+
// Built the first time the folder opens, so a closed folder costs nothing however large.
|
|
232
|
+
children: Row[] | null;
|
|
233
|
+
depth: number;
|
|
234
|
+
element: Element;
|
|
235
|
+
// How many of the row's siblings are hidden, shared by them all; 'position' and 'size' count them in.
|
|
236
|
+
gaps: Signal<number>;
|
|
237
|
+
// Set on a folder folded into a compact row: the row, which is its last folder's. A folded folder is laid out
|
|
238
|
+
// like that row, in its place, and reads as open.
|
|
239
|
+
host?: Row;
|
|
240
|
+
id: string;
|
|
241
|
+
key: number;
|
|
242
|
+
locked: boolean;
|
|
243
|
+
// Set on the row standing in for a lazy folder's children while they load, or after they failed to.
|
|
244
|
+
notice?: Notice;
|
|
245
|
+
open: Signal<boolean> | null;
|
|
246
|
+
parent: Row | null;
|
|
247
|
+
position: number;
|
|
248
|
+
// The tint color, taken from the row's folder when no scope matches the row itself.
|
|
249
|
+
scope: string;
|
|
250
|
+
// Set on a compact row: the folders folded into it, outermost first. 'parent' and 'depth' are the row's on
|
|
251
|
+
// screen, so it sits where the first of them would.
|
|
252
|
+
segments?: Row[];
|
|
253
|
+
size: number;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
type Scope = {
|
|
257
|
+
color: string;
|
|
258
|
+
// A pattern is tested against the element's path from the tree's root, like 'src/components/index.ts', or from
|
|
259
|
+
// its own root under 'roots'.
|
|
260
|
+
match: RegExp | ((element: Element) => boolean);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
type Snapshot = {
|
|
264
|
+
expanded?: string[];
|
|
265
|
+
// The viewport's scrollTop, in pixels.
|
|
266
|
+
scroll?: number;
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
type State = {
|
|
270
|
+
// The primary selection: the anchor, and the row the highlight rests on.
|
|
271
|
+
selected: string;
|
|
272
|
+
// Every selected id, primary included; replaced as a whole on each change.
|
|
273
|
+
selection?: ReadonlySet<string>;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
// One per unit, made once: a formatter is slow to construct and the tooltip formats a size on every open. Short bytes
|
|
278
|
+
// read '12 byte', so whole bytes are spelled out.
|
|
279
|
+
const BYTES = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte'].map((unit, i) => new Intl.NumberFormat(undefined, {
|
|
280
|
+
maximumFractionDigits: 1,
|
|
281
|
+
style: 'unit',
|
|
282
|
+
unit,
|
|
283
|
+
unitDisplay: i ? 'short' : 'long'
|
|
284
|
+
}));
|
|
285
|
+
|
|
286
|
+
// Engines cap how many arguments a spread may pass, so very large folders are inserted in slices.
|
|
287
|
+
const CHUNK = 8192;
|
|
288
|
+
|
|
289
|
+
const GUIDES: string[] = [];
|
|
290
|
+
|
|
291
|
+
const LETTERS: Record<Status, string> = {
|
|
292
|
+
added: 'A',
|
|
293
|
+
conflict: '!',
|
|
294
|
+
deleted: 'D',
|
|
295
|
+
ignored: '',
|
|
296
|
+
modified: 'M',
|
|
297
|
+
renamed: 'R',
|
|
298
|
+
untracked: 'U'
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const PREVIEW: Open = { mode: 'preview', side: false };
|
|
302
|
+
|
|
303
|
+
// Precedence, lowest first: a row shows the highest of its own tone and, for a folder, those of its contents.
|
|
304
|
+
// Ignored stays with the row; a folder of ignored files isn't itself ignored.
|
|
305
|
+
const RANK: Tone[] = ['ignored', 'added', 'modified', 'warning', 'deleted', 'conflict', 'error'];
|
|
306
|
+
|
|
307
|
+
// Long enough that holding an arrow key down opens only the file it stops on.
|
|
308
|
+
const REST = 200;
|
|
309
|
+
|
|
310
|
+
const ROW = Symbol();
|
|
311
|
+
|
|
312
|
+
const TONES: Record<Status, Tone> = {
|
|
313
|
+
added: 'added',
|
|
314
|
+
conflict: 'conflict',
|
|
315
|
+
deleted: 'deleted',
|
|
316
|
+
ignored: 'ignored',
|
|
317
|
+
modified: 'modified',
|
|
318
|
+
renamed: 'added',
|
|
319
|
+
untracked: 'added'
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// Open delay for the path tooltip, so it stays out of the way of a pointer passing over the tree.
|
|
323
|
+
const TOOLTIP = 600;
|
|
324
|
+
|
|
325
|
+
// Long enough to type "pa" at a normal pace, short enough that a fresh letter a moment later starts a new search.
|
|
326
|
+
const TYPEAHEAD = 500;
|
|
327
|
+
|
|
328
|
+
const dates = new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' });
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
let uid = 0;
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
// Problems show in the row's tone alone; the badge carries, in order, the diff stats, the consumer's badges, the
|
|
335
|
+
// git letters (index then working tree, or a folder's dot for what's inside it) and the editor marker. 'rollup' is
|
|
336
|
+
// null for a file.
|
|
337
|
+
function badge(decoration: Decoration | undefined, lines: number[], rollup: Tone | '' | null) {
|
|
338
|
+
let editor = decoration?.editor,
|
|
339
|
+
parts: Part[] = [],
|
|
340
|
+
staged = decoration?.staged,
|
|
341
|
+
status = decoration?.status;
|
|
342
|
+
|
|
343
|
+
if (lines[0]) {
|
|
344
|
+
parts.push({ text: `+${lines[0]}`, tone: 'additions' });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (lines[1]) {
|
|
348
|
+
parts.push({ text: `−${lines[1]}`, tone: 'deletions' });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for (let custom of decoration?.badges ?? []) {
|
|
352
|
+
parts.push({ color: custom.color, text: custom.text, title: custom.tooltip });
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (rollup === null && staged && LETTERS[staged]) {
|
|
356
|
+
parts.push({ staged: true, text: LETTERS[staged], title: 'Staged', tone: TONES[staged] });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (rollup === null && status && LETTERS[status]) {
|
|
360
|
+
parts.push({ text: LETTERS[status], tone: TONES[status] });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (decoration?.submodule) {
|
|
364
|
+
parts.push({ text: 'S', tone: 'submodule' });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (rollup) {
|
|
368
|
+
parts.push({ text: '●', tone: rollup });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (editor?.unsaved || editor?.open) {
|
|
372
|
+
parts.push({ text: editor.unsaved ? '●' : '○', tone: editor.unsaved ? 'unsaved' : 'open' });
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return parts;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Steps of 1024, as VS Code reports sizes.
|
|
379
|
+
function bytes(value: number) {
|
|
380
|
+
let unit = 0;
|
|
381
|
+
|
|
382
|
+
while (value >= 1024 && unit < BYTES.length - 1) {
|
|
383
|
+
value /= 1024;
|
|
384
|
+
unit++;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return BYTES[unit].format(value);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function chip(part: Part) {
|
|
391
|
+
return html`
|
|
392
|
+
<span
|
|
393
|
+
class='file-tree-badge-part ${part.staged && 'file-tree-badge-part--staged'} ${part.tone && `file-tree-badge-part--${part.tone}`}'
|
|
394
|
+
${{ style: part.color && `color: ${part.color}` }}
|
|
395
|
+
${!!part.title && { title: part.title }}
|
|
396
|
+
>
|
|
397
|
+
${part.text}
|
|
398
|
+
</span>
|
|
399
|
+
`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// The name with the characters 'query' matches marked, a run of them to a mark; the bare name when there are none.
|
|
403
|
+
function emphasis(name: string, query: string) {
|
|
404
|
+
let match = query ? fuzzy(query, name) : null;
|
|
405
|
+
|
|
406
|
+
if (!match) {
|
|
407
|
+
return name;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
let at = 0,
|
|
411
|
+
indices = match.indices,
|
|
412
|
+
out: Renderable<unknown>[] = [];
|
|
413
|
+
|
|
414
|
+
for (let i = 0, n = indices.length; i < n;) {
|
|
415
|
+
let end = indices[i] + 1,
|
|
416
|
+
start = indices[i];
|
|
417
|
+
|
|
418
|
+
while (++i < n && indices[i] === end) {
|
|
419
|
+
end++;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (start > at) {
|
|
423
|
+
out.push(name.slice(at, start));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
out.push(html`<mark class='file-tree-match'>${name.slice(start, end)}</mark>`);
|
|
427
|
+
at = end;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (at < name.length) {
|
|
431
|
+
out.push(name.slice(at));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return out;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// A search mark as classes on 'block': every match tinted, the current one outlined as well.
|
|
438
|
+
function found(block: string, mark: string | false) {
|
|
439
|
+
if (!mark) {
|
|
440
|
+
return mark;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return mark === 'current' ? `${block}--match ${block}--match-current` : `${block}--match`;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function folder(element: Element) {
|
|
447
|
+
return element.type ? element.type === 'folder' : Array.isArray(element.children);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Shadows repeating a row's parent guide under each further ancestor, read by the row's '::before'.
|
|
451
|
+
function guides(depth: number) {
|
|
452
|
+
let out = GUIDES[depth];
|
|
453
|
+
|
|
454
|
+
if (out !== undefined) {
|
|
455
|
+
return out;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
let parts: string[] = [];
|
|
459
|
+
|
|
460
|
+
for (let i = 1; i < depth; i++) {
|
|
461
|
+
parts.push(`calc(var(--indent) * ${-i} * var(--guide-direction)) 0`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return GUIDES[depth] = parts.join(', ') || 'none';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// The rank a row lends its folders: a deleted file is a change inside them, not a loss of them.
|
|
468
|
+
function lift(rank: number) {
|
|
469
|
+
return rank === RANK.indexOf('deleted') ? RANK.indexOf('modified') : rank;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// A middle click pins a file rather than starting the browser's autoscroll.
|
|
473
|
+
function middle(event: MouseEvent) {
|
|
474
|
+
if (event.button === 1) {
|
|
475
|
+
event.preventDefault();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function path(row: Row) {
|
|
480
|
+
return [...ancestors(row), row].map((node) => node.element.name).join('/');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// The folder whose segment of a compact row was pressed, or the row's own.
|
|
484
|
+
function pick(row: Row, event: MouseEvent) {
|
|
485
|
+
return (event.target as HTMLElement).closest<Segment<Row>>('.file-tree-segment')?.[SEGMENT] ?? row;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function place(row: Row) {
|
|
489
|
+
let out: number[] = [];
|
|
490
|
+
|
|
491
|
+
for (let node: Row | null = row; node; node = node.parent) {
|
|
492
|
+
out.unshift(node.position);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return out;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function rank(decoration: Decoration | undefined) {
|
|
499
|
+
if (!decoration) {
|
|
500
|
+
return -1;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (decoration.errors) {
|
|
504
|
+
return RANK.indexOf('error');
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// The working tree says what's on disk now; the index speaks only when the working tree is clean.
|
|
508
|
+
let value = decoration.status ?? decoration.staged,
|
|
509
|
+
status = value ? RANK.indexOf(TONES[value]) : -1;
|
|
510
|
+
|
|
511
|
+
return decoration.warnings ? Math.max(status, RANK.indexOf('warning')) : status;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// The row a reader still sees once the folders above 'row' close: its outermost closed ancestor, or itself.
|
|
515
|
+
function shown(row: Row) {
|
|
516
|
+
let target = row;
|
|
517
|
+
|
|
518
|
+
for (let node = row.parent; node; node = node.parent) {
|
|
519
|
+
if (!peek(node.open!)) {
|
|
520
|
+
target = node;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return target;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// The folders to open so 'id' is on screen: its ancestors, and itself when it's a folder a click could open.
|
|
528
|
+
function trail(index: Map<string, Entry>, id: string) {
|
|
529
|
+
let entry = index.get(id),
|
|
530
|
+
out: string[] = [];
|
|
531
|
+
|
|
532
|
+
if (!entry) {
|
|
533
|
+
return out;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (folder(entry.element) && entry.element.selectable !== false) {
|
|
537
|
+
out.push(id);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
for (let parent = entry.parent; parent !== null; parent = index.get(parent)!.parent) {
|
|
541
|
+
out.push(parent);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return out;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function twistie(event: MouseEvent) {
|
|
548
|
+
return (event.target as HTMLElement).closest('.file-tree-twistie') !== null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
export default ({
|
|
553
|
+
compact = true,
|
|
554
|
+
controller,
|
|
555
|
+
create,
|
|
556
|
+
decorations: providers,
|
|
557
|
+
display = {},
|
|
558
|
+
drag,
|
|
559
|
+
editor,
|
|
560
|
+
elements,
|
|
561
|
+
empty,
|
|
562
|
+
exclude = [],
|
|
563
|
+
expand: unfold = 'click',
|
|
564
|
+
expanded = [],
|
|
565
|
+
export: exporter,
|
|
566
|
+
find = 'highlight',
|
|
567
|
+
highlight: phrase = '',
|
|
568
|
+
history,
|
|
569
|
+
icon: draw,
|
|
570
|
+
icons = {},
|
|
571
|
+
indicator = 'always',
|
|
572
|
+
label = 'Files',
|
|
573
|
+
load,
|
|
574
|
+
menu,
|
|
575
|
+
open: view,
|
|
576
|
+
operations,
|
|
577
|
+
path: address = (element) => element.id,
|
|
578
|
+
preview,
|
|
579
|
+
rename,
|
|
580
|
+
reveal: autoreveal = 'on',
|
|
581
|
+
roots = false,
|
|
582
|
+
scopes = [],
|
|
583
|
+
searched,
|
|
584
|
+
select,
|
|
585
|
+
selected,
|
|
586
|
+
shortcuts,
|
|
587
|
+
snapshot,
|
|
588
|
+
sort = 'folders',
|
|
589
|
+
state = reactive({ selected: selected ?? '', selection: new Set<string>() }),
|
|
590
|
+
sticky = true,
|
|
591
|
+
toggle,
|
|
592
|
+
tooltip: hint = true,
|
|
593
|
+
typing = 'typeahead',
|
|
594
|
+
validate,
|
|
595
|
+
...attributes
|
|
596
|
+
}: A) => {
|
|
597
|
+
let built = new Map<string, Row>(),
|
|
598
|
+
// Whatever the tree reads while it's built is read untracked: built inside a caller's computation, it would
|
|
599
|
+
// otherwise rebuild the tree on every selection or display change.
|
|
600
|
+
chosen = untrack(() => state.selected),
|
|
601
|
+
clipboard = new Clipboard<Element>(),
|
|
602
|
+
compare = comparator(sort, folder),
|
|
603
|
+
// Descendant tones per folder, one count per rank, so a change walks its ancestors instead of the tree.
|
|
604
|
+
counts = new Map<string, number[]>(),
|
|
605
|
+
// The primary selection as rows read it: each asks about its own id alone, so a change re-renders the row it
|
|
606
|
+
// leaves and the one it reaches rather than every row. Kept in step with 'state.selected' while connected.
|
|
607
|
+
current = signal(chosen),
|
|
608
|
+
decorations = providers && merge([providers].flat()),
|
|
609
|
+
// Lines added and removed per shown row; 'totals' sums them per folder, kept like 'counts'.
|
|
610
|
+
diffs = new Map<string, number[]>(),
|
|
611
|
+
// The open name input, if any; a new item's row lives only in 'rows', never among its folder's children.
|
|
612
|
+
draft = signal<Draft<Row> | null>(null),
|
|
613
|
+
excluded = glob(exclude),
|
|
614
|
+
model = elements instanceof Elements ? elements : new Elements(elements),
|
|
615
|
+
filtered = untrack(() => filter(model.elements, display.dotfiles !== false, excluded, roots)),
|
|
616
|
+
ghosts = new ReactiveArray<Ghost>(),
|
|
617
|
+
hidden = untrack(() => new Set([...filtered, ...(decorations?.keys() ?? [])].filter(gone))),
|
|
618
|
+
id = `file-tree-${++uid}`,
|
|
619
|
+
index = model.index,
|
|
620
|
+
initial = new Set([
|
|
621
|
+
...(snapshot?.expanded ?? expanded),
|
|
622
|
+
...(roots && !snapshot?.expanded ? model.elements.map((element) => element.id) : []),
|
|
623
|
+
...(autoreveal === 'off' ? [] : trail(index, chosen))
|
|
624
|
+
]),
|
|
625
|
+
key = 0,
|
|
626
|
+
// The primary selection last acted on, so one written while disconnected is revealed on connect.
|
|
627
|
+
known = chosen,
|
|
628
|
+
loader = load ? new Loader(load, loaded) : null,
|
|
629
|
+
marks = new Map<string, Signal<Mark>>(),
|
|
630
|
+
// The row under each empty root saying so, by root id; kept so the list finds the same row every time.
|
|
631
|
+
notes = new Map<string, Row>(),
|
|
632
|
+
opened = 0,
|
|
633
|
+
own = new Map<string, number>(),
|
|
634
|
+
// Per sibling list, how many of its rows are hidden before each place, so a row finds its place in "3 of 5"
|
|
635
|
+
// without walking every row before it; 'at' is the 'shifts' it was counted at.
|
|
636
|
+
places = new WeakMap<Row[], { at: number; before: Int32Array }>(),
|
|
637
|
+
// What 'highlight' asks the names to mark; a getter given for it writes this while connected.
|
|
638
|
+
given = signal(typeof phrase === 'function' ? untrack(phrase) : phrase),
|
|
639
|
+
// The search the rows show, empty while the find bar is closed.
|
|
640
|
+
query = signal(''),
|
|
641
|
+
remap = icons ? icons.remap ?? {} : {},
|
|
642
|
+
render = draw ?? (icons ? renderer(icons) : () => false),
|
|
643
|
+
// The rows a folder is opening onto, by place within it; emptied when the motion settles, so rows scrolled
|
|
644
|
+
// back into view later arrive as they are.
|
|
645
|
+
revealing = new Map<Row, number>(),
|
|
646
|
+
search = find === 'off' ? null : new Finder(find, query, {
|
|
647
|
+
compare,
|
|
648
|
+
// A compact row's selected segment is where the cursor is.
|
|
649
|
+
cursor: () => cursor && segment(cursor).id,
|
|
650
|
+
elements: model.elements,
|
|
651
|
+
folder,
|
|
652
|
+
go: (id) => reveal(id, true),
|
|
653
|
+
gone,
|
|
654
|
+
lazy: !!load,
|
|
655
|
+
leave: () => viewport?.focus({ preventScroll: true }),
|
|
656
|
+
searched,
|
|
657
|
+
shows: (id) => {
|
|
658
|
+
let row = built.get(id);
|
|
659
|
+
|
|
660
|
+
return !!row && !hidden.has(id) && shown(row) === row;
|
|
661
|
+
},
|
|
662
|
+
sift: (folders) => {
|
|
663
|
+
sift(folders);
|
|
664
|
+
slot.flush();
|
|
665
|
+
}
|
|
666
|
+
}),
|
|
667
|
+
// The store's version this tree last caught up with.
|
|
668
|
+
seen = model.version,
|
|
669
|
+
// The display flags last applied, so flags changed while disconnected are applied on connect.
|
|
670
|
+
settings = untrack(modes),
|
|
671
|
+
// Bumped whenever a row is hidden or shown, so the places counted before are counted again.
|
|
672
|
+
shifts = 0,
|
|
673
|
+
// A folder kept out of its compact row while a name input is open in it or on it.
|
|
674
|
+
split: string | null = null,
|
|
675
|
+
// Where a fold motion's layer sits over the viewport, and the folder's bottom edge within it.
|
|
676
|
+
stage = reactive({ edge: 0, height: 0, left: 0, top: 0, width: 0 }),
|
|
677
|
+
// The key of the row a fold motion runs from; 0 while none runs.
|
|
678
|
+
sweep = signal(0),
|
|
679
|
+
top = build(model.elements, 0, null),
|
|
680
|
+
totals = new Map<string, number[]>(),
|
|
681
|
+
// The folders filtering has reached, and whether it opened them, so ending it closes those alone.
|
|
682
|
+
unfolded = new Map<string, boolean>(),
|
|
683
|
+
// Filled before the slot exists: rows inserted at the top of a live slot read as arriving above the reader.
|
|
684
|
+
rows = reactive(visible(top)),
|
|
685
|
+
cursor: Row | null = rows.find((row) => holds(row, (id) => id === chosen)) ?? rows[0] ?? null,
|
|
686
|
+
dragging = (drag || exporter || operations?.import) && draggable<Row>({ drag, export: exporter, import: operations?.import }, {
|
|
687
|
+
// Pressing a selected row drags the whole selection, as in VS Code; any other row goes alone. Roots stay, and
|
|
688
|
+
// a folder selected along with one folded above it in a compact row travels inside it.
|
|
689
|
+
grab: (row) => (selection.has(row.id) ? [...selection.ids].map((id) => built.get(id)) : [row])
|
|
690
|
+
.filter((row): row is Row => row !== undefined && !header(row) && !ancestors(row).some((node) => selection.has(node.id) && !header(node))),
|
|
691
|
+
host: (row) => row.host ?? row,
|
|
692
|
+
// Down through each open folder's last shown row, rather than building the whole box to read its end.
|
|
693
|
+
last: (row) => {
|
|
694
|
+
let out = row;
|
|
695
|
+
|
|
696
|
+
while (out.open && peek(out.open)) {
|
|
697
|
+
let list = branch(out),
|
|
698
|
+
next: Row | undefined;
|
|
699
|
+
|
|
700
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
701
|
+
if (!hidden.has(list[i].id)) {
|
|
702
|
+
next = list[i];
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (!next) {
|
|
708
|
+
// Nothing shown inside, so only a new item's input or an empty root's note can follow.
|
|
709
|
+
let inner = visible(list);
|
|
710
|
+
|
|
711
|
+
return inner[inner.length - 1] ?? out;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
out = next;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return out;
|
|
718
|
+
},
|
|
719
|
+
open: (row) => fold(row, true),
|
|
720
|
+
row: (element) => (element as Segment<Row>)[SEGMENT] ?? (element as Rendered)[ROW],
|
|
721
|
+
top: !roots,
|
|
722
|
+
viewport: () => viewport
|
|
723
|
+
}),
|
|
724
|
+
// The key of the cursor's row, read per row like 'current'; 0 with no rows.
|
|
725
|
+
focused = signal(cursor?.key ?? 0),
|
|
726
|
+
// An id the tree itself just made primary: the reveal an outside write gets would pull focus onto it.
|
|
727
|
+
quiet: string | null = null,
|
|
728
|
+
// One tooltip for the whole tree, gliding from row to row; rows come and go as they scroll, so it's bound to
|
|
729
|
+
// the viewport rather than to each row.
|
|
730
|
+
tip = tooltip.shared({ delay: { open: TOOLTIP }, direction: 'e' }),
|
|
731
|
+
typed = '',
|
|
732
|
+
typedAt = 0,
|
|
733
|
+
// 'motion' is a fold motion's direction.
|
|
734
|
+
ui = reactive({ any: opened > 0, motion: '' as '' | 'close' | 'open', motionRows: 0 }),
|
|
735
|
+
selection = new Selection((ids) => {
|
|
736
|
+
state.selection = ids;
|
|
737
|
+
}),
|
|
738
|
+
slot = html.virtual(rows, (row) => {
|
|
739
|
+
if (row.notice) {
|
|
740
|
+
return placeholder(row.notice, {
|
|
741
|
+
'aria-level': String(row.depth + 1),
|
|
742
|
+
class: [
|
|
743
|
+
() => signal.selector(focused, row.key) && 'file-tree-row--focused',
|
|
744
|
+
() => revealing.has(row) && ui.motion === 'open' && 'file-tree-row--reveal'
|
|
745
|
+
],
|
|
746
|
+
id: `${id}-${row.key}`,
|
|
747
|
+
onclick: () => {
|
|
748
|
+
focus(row);
|
|
749
|
+
retry(row);
|
|
750
|
+
},
|
|
751
|
+
style: `--depth: ${row.depth}; --guides: ${guides(row.depth)}; --reveal-index: ${revealing.get(row) ?? 0};`
|
|
752
|
+
}, dragging?.row(row) ?? {}, onscreen(row));
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
let mark = decorations ? track(row.id) : null,
|
|
756
|
+
open = row.open;
|
|
757
|
+
|
|
758
|
+
return html`
|
|
759
|
+
<div
|
|
760
|
+
aria-level='${row.depth + 1}'
|
|
761
|
+
class='file-tree-row ${row.locked && '--disabled'} ${header(row) && 'file-tree-row--root'} ${() => active(row) && '--active'} ${() => holds(row, (id) => clipboard.marked.read(id)) && 'file-tree-row--cut'} ${() => holds(row, (id) => selection.read(id)) && !active(row) && 'file-tree-row--selected'}'
|
|
762
|
+
id='${id}-${row.key}'
|
|
763
|
+
role='treeitem'
|
|
764
|
+
style='--depth: ${row.depth}; --guides: ${guides(row.depth)}; --reveal-index: ${revealing.get(row) ?? 0};'
|
|
765
|
+
${row.locked && { 'aria-disabled': 'true' }}
|
|
766
|
+
${open && { 'aria-expanded': () => read(open) ? 'true' : 'false' }}
|
|
767
|
+
${mark && { class: () => read(mark).tone && `file-tree-row--${read(mark).tone}` }}
|
|
768
|
+
${search && { class: () => found('file-tree-row', matched(row)) }}
|
|
769
|
+
${!!row.scope && { class: 'file-tree-row--scope', style: `--scope: ${row.scope}` }}
|
|
770
|
+
${dragging?.row(row)}
|
|
771
|
+
${onscreen(row)}
|
|
772
|
+
${{
|
|
773
|
+
'aria-label': mark ? () => read(mark).label : spoken({ ...row.element, name: caption(row), root: header(row) }),
|
|
774
|
+
'aria-posinset': () => position(row),
|
|
775
|
+
'aria-selected': () => holds(row, (id) => selection.read(id)) ? 'true' : 'false',
|
|
776
|
+
'aria-setsize': () => row.size - read(row.gaps),
|
|
777
|
+
// Each tests its own row first, so only the rows concerned follow the cursor and the motion.
|
|
778
|
+
class: [
|
|
779
|
+
() => signal.selector(focused, row.key) && 'file-tree-row--focused',
|
|
780
|
+
() => signal.selector(sweep, row.key) && ui.motion === 'close' && 'file-tree-row--motion',
|
|
781
|
+
() => revealing.has(row) && ui.motion === 'open' && 'file-tree-row--reveal'
|
|
782
|
+
],
|
|
783
|
+
onauxclick: (event: MouseEvent) => press(row, event),
|
|
784
|
+
onclick: (event: MouseEvent) => press(row, event),
|
|
785
|
+
ondblclick: (event: MouseEvent) => press(row, event)
|
|
786
|
+
}}
|
|
787
|
+
>
|
|
788
|
+
${contents(row, mark, false)}
|
|
789
|
+
</div>
|
|
790
|
+
`;
|
|
791
|
+
}),
|
|
792
|
+
// Copies of the pinned folders' rows; the tree's own stay in the list beneath, the highlight with them. The
|
|
793
|
+
// highlight draws under the stack, so a copy shows its own hover and selection.
|
|
794
|
+
pins = stick(rows, sticky, (row) => {
|
|
795
|
+
let mark = decorations ? track(row.id) : null;
|
|
796
|
+
|
|
797
|
+
return html`
|
|
798
|
+
<div
|
|
799
|
+
aria-expanded='true'
|
|
800
|
+
class='file-tree-row file-tree-row--pinned ${row.locked && '--disabled'} ${header(row) && 'file-tree-row--root'} ${() => active(row) && 'file-tree-row--selected'}'
|
|
801
|
+
style='--depth: ${row.depth}; --guides: ${guides(row.depth)};'
|
|
802
|
+
${mark && { class: () => read(mark).tone && `file-tree-row--${read(mark).tone}` }}
|
|
803
|
+
${{
|
|
804
|
+
class: () => signal.selector(focused, row.key) && 'file-tree-row--focused',
|
|
805
|
+
onclick: () => unpin(row)
|
|
806
|
+
}}
|
|
807
|
+
>
|
|
808
|
+
${contents(row, mark, true)}
|
|
809
|
+
</div>
|
|
810
|
+
`;
|
|
811
|
+
}, height),
|
|
812
|
+
anchor: Row | null = null,
|
|
813
|
+
detach: VoidFunction | undefined,
|
|
814
|
+
forget: VoidFunction | undefined,
|
|
815
|
+
// The frame a fold motion starts on; 0 while none runs.
|
|
816
|
+
motion = 0,
|
|
817
|
+
root: HTMLElement | undefined,
|
|
818
|
+
timer: ReturnType<typeof setTimeout> | undefined,
|
|
819
|
+
// The marks a batch of decorations reached, refreshed once each as it ends; null outside one.
|
|
820
|
+
touched: Set<string> | null = null,
|
|
821
|
+
unsubscribe: VoidFunction | undefined,
|
|
822
|
+
unwatch: VoidFunction | undefined,
|
|
823
|
+
viewport: HTMLElement | undefined;
|
|
824
|
+
|
|
825
|
+
selection.replace([...(untrack(() => state.selection) ?? []), ...(chosen ? [chosen] : [])]);
|
|
826
|
+
persist();
|
|
827
|
+
|
|
828
|
+
// The tooltip's content: the full path, which also shows a name cut short in its row, then size and date. Built
|
|
829
|
+
// only once the tooltip opens, since the pointer crossing a row's parts asks for it again and again.
|
|
830
|
+
function about(row: Row) {
|
|
831
|
+
return () => {
|
|
832
|
+
let element = row.element,
|
|
833
|
+
full = path(row),
|
|
834
|
+
meta: string[] = [];
|
|
835
|
+
|
|
836
|
+
if (element.size !== undefined) {
|
|
837
|
+
meta.push(bytes(element.size));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
if (element.modified !== undefined) {
|
|
841
|
+
meta.push(dates.format(element.modified));
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return html`
|
|
845
|
+
<span class='file-tree-tooltip-path'>${full.slice(0, full.length - element.name.length)}<strong>${element.name}</strong></span>
|
|
846
|
+
${meta.length > 0 && html`<span class='file-tree-tooltip-meta'>${meta.join(' · ')}</span>`}
|
|
847
|
+
`;
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
controller?.({
|
|
852
|
+
collapseAll: () => all(false),
|
|
853
|
+
expandAll: () => all(true),
|
|
854
|
+
find: (text) => search?.open(text),
|
|
855
|
+
next: (kind) => jump(kind, 1),
|
|
856
|
+
previous: (kind) => jump(kind, -1),
|
|
857
|
+
search: (text) => search?.set(text)
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
// A folder given 'toggle' false is only selected. 'subject' is what's selected: in a compact row, the folder of
|
|
861
|
+
// the segment pressed, which opening and closing the row leaves be.
|
|
862
|
+
function activate(row: Row, open: Open, toggle = true, subject = segment(row)) {
|
|
863
|
+
if (row.notice) {
|
|
864
|
+
retry(row);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (row.locked) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
clearTimeout(timer);
|
|
873
|
+
anchor = row;
|
|
874
|
+
selection.replace([subject.id]);
|
|
875
|
+
state.selected = subject.id;
|
|
876
|
+
select?.(subject.element);
|
|
877
|
+
|
|
878
|
+
if (!row.open) {
|
|
879
|
+
view?.(row.element, open);
|
|
880
|
+
}
|
|
881
|
+
else if (toggle) {
|
|
882
|
+
fold(row, !peek(row.open));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Selected itself, or through one of the folders folded into it.
|
|
887
|
+
function active(row: Row) {
|
|
888
|
+
return holds(row, (id) => signal.selector(current, id));
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Opens every folder a click could open, or closes them all; within 'scope' and its folders alone when given.
|
|
892
|
+
function all(value: boolean, scope?: Row) {
|
|
893
|
+
let at = scope ? rows.indexOf(scope) : -1;
|
|
894
|
+
|
|
895
|
+
if (scope && (!scope.open || scope.locked || at === -1)) {
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
settle();
|
|
900
|
+
|
|
901
|
+
let count = scope ? (peek(scope.open!) ? visible(branch(scope)).length : 0) : rows.length,
|
|
902
|
+
stack = scope ? [scope] : [...top];
|
|
903
|
+
|
|
904
|
+
while (stack.length) {
|
|
905
|
+
let row = stack.pop()!;
|
|
906
|
+
|
|
907
|
+
if (!row.open) {
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Locked folders stay shut, matching how a click on them does nothing. An empty root opens onto its note.
|
|
912
|
+
let open = value && !row.locked && (header(row) || !!row.element.children?.length);
|
|
913
|
+
|
|
914
|
+
if (peek(row.open) !== open) {
|
|
915
|
+
write(row.open, open);
|
|
916
|
+
opened += open ? 1 : -1;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
if (!open && loader?.forget(row.id)) {
|
|
920
|
+
row.children = null;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// Closing never builds a folder that was never opened.
|
|
924
|
+
let children = open ? branch(row) : (row.children ?? []);
|
|
925
|
+
|
|
926
|
+
for (let i = 0, n = children.length; i < n; i++) {
|
|
927
|
+
stack.push(children[i]);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Replaced in place; clearing first would make everything after read as inserted above the reader.
|
|
932
|
+
swap(at + 1, at + 1 + count, scope ? (peek(scope.open!) ? visible(branch(scope)) : []) : visible(top));
|
|
933
|
+
prune();
|
|
934
|
+
ui.any = opened > 0;
|
|
935
|
+
|
|
936
|
+
if (cursor) {
|
|
937
|
+
focus(shown(cursor));
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
persist();
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// Opens a name input on the target, undefined meaning the focused row: to rename it, or for a new item inside it
|
|
944
|
+
// when it's a folder and beside it otherwise; null puts a new item at the top level.
|
|
945
|
+
function begin(action: Kind | 'rename', target?: string | null) {
|
|
946
|
+
close(false);
|
|
947
|
+
|
|
948
|
+
if (target) {
|
|
949
|
+
reveal(target, true);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
let row = target === null ? null : target === undefined ? cursor && segment(cursor) : built.get(target);
|
|
953
|
+
|
|
954
|
+
if (row === undefined) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// A folder in the middle of a compact row leaves it for the edit, so the input and a new item's row sit
|
|
959
|
+
// under a row of its own; renaming a compact row's last folder edits its last segment in place.
|
|
960
|
+
if (row?.host) {
|
|
961
|
+
row = separate(row);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (action === 'rename') {
|
|
965
|
+
if (!rename || !row || row.locked || header(row)) {
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
write(draft, {
|
|
970
|
+
busy: signal(false),
|
|
971
|
+
element: row.element,
|
|
972
|
+
from: row,
|
|
973
|
+
kind: folder(row.element) ? 'folder' : 'file',
|
|
974
|
+
message: signal(''),
|
|
975
|
+
parent: row.parent,
|
|
976
|
+
range: null,
|
|
977
|
+
row,
|
|
978
|
+
value: row.element.name
|
|
979
|
+
});
|
|
980
|
+
move(rows.indexOf(row), 'end');
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (!create) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// A file, or a folder a click can't open, takes the new item beside it. Roots hold the top level, so one
|
|
989
|
+
// asked for there lands in the first.
|
|
990
|
+
let parent = row?.open && !row.locked ? row : (row?.parent ?? (roots ? top[0] ?? null : null)),
|
|
991
|
+
stub: Row = {
|
|
992
|
+
children: null,
|
|
993
|
+
depth: parent ? parent.depth + 1 : 0,
|
|
994
|
+
element: { id: `${id}-draft`, name: '', type: action },
|
|
995
|
+
gaps: signal(0),
|
|
996
|
+
id: `${id}-draft`,
|
|
997
|
+
key: ++key,
|
|
998
|
+
locked: false,
|
|
999
|
+
open: action === 'folder' ? signal(false) : null,
|
|
1000
|
+
parent,
|
|
1001
|
+
position: 1,
|
|
1002
|
+
scope: parent?.scope ?? '',
|
|
1003
|
+
size: (parent ? branch(parent) : top).length + 1
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
if (roots && !parent) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
write(draft, {
|
|
1011
|
+
busy: signal(false),
|
|
1012
|
+
element: null,
|
|
1013
|
+
from: cursor,
|
|
1014
|
+
kind: action,
|
|
1015
|
+
message: signal(''),
|
|
1016
|
+
parent,
|
|
1017
|
+
range: null,
|
|
1018
|
+
row: stub,
|
|
1019
|
+
value: ''
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
// Opening the folder lists the stub along with its rows.
|
|
1023
|
+
if (parent && !peek(parent.open!)) {
|
|
1024
|
+
expand(parent, true);
|
|
1025
|
+
}
|
|
1026
|
+
else {
|
|
1027
|
+
rows.splice(parent ? rows.indexOf(parent) + 1 : 0, 0, stub);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
move(rows.indexOf(stub), 'end');
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// The note under an open root holding nothing; made again when the root's row is.
|
|
1034
|
+
function blank(root: Row) {
|
|
1035
|
+
let row = notes.get(root.id);
|
|
1036
|
+
|
|
1037
|
+
if (!row || row.parent !== root) {
|
|
1038
|
+
notes.set(root.id, row = notice(root, { type: 'empty' }));
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
return row;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// A folder given without children, when there's a loader, has a notice for them until they arrive.
|
|
1045
|
+
function branch(row: Row) {
|
|
1046
|
+
return row.children ??= loader && !row.element.children
|
|
1047
|
+
? [notice(row, loader.request(row.element))]
|
|
1048
|
+
: build(row.element.children ?? [], row.depth + 1, row);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function build(elements: Element[], depth: number, parent: Row | null) {
|
|
1052
|
+
let arranged = compare && !(roots && !parent) ? [...elements].sort(compare) : elements,
|
|
1053
|
+
gaps = group(arranged, parent),
|
|
1054
|
+
n = arranged.length,
|
|
1055
|
+
out: Row[] = new Array(n),
|
|
1056
|
+
// Paths run from within each root under 'roots', as exclude patterns match; a root has none of its own.
|
|
1057
|
+
prefix = scopes.length && parent ? `${path(parent)}/` : '',
|
|
1058
|
+
unseen = 0;
|
|
1059
|
+
|
|
1060
|
+
if (roots) {
|
|
1061
|
+
prefix = prefix.slice(prefix.indexOf('/') + 1);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
for (let i = 0; i < n; i++) {
|
|
1065
|
+
let element = arranged[i],
|
|
1066
|
+
full = roots && !parent ? '' : prefix + element.name,
|
|
1067
|
+
merged = false,
|
|
1068
|
+
segments: Row[] = [],
|
|
1069
|
+
tint = scopes.length ? scope(element, full, parent?.scope ?? '') : '';
|
|
1070
|
+
|
|
1071
|
+
if (hidden.has(element.id)) {
|
|
1072
|
+
unseen++;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// A folder holding just one folder shares its row with it, and so on down the chain; the row is the last
|
|
1076
|
+
// folder's, in the first one's place.
|
|
1077
|
+
for (let next = joined(element); next; next = joined(element)) {
|
|
1078
|
+
let previous = built.get(element.id);
|
|
1079
|
+
|
|
1080
|
+
merged ||= !!previous?.open && !previous.host && peek(previous.open);
|
|
1081
|
+
segments.push(lay({ depth, element, gaps, parent, position: i + 1, scope: tint, segments: undefined, size: n }, true));
|
|
1082
|
+
element = next;
|
|
1083
|
+
full = `${full}/${element.name}`;
|
|
1084
|
+
tint = scopes.length ? scope(element, full, tint) : '';
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
let fresh = !built.has(element.id),
|
|
1088
|
+
row = out[i] = lay({
|
|
1089
|
+
depth,
|
|
1090
|
+
element,
|
|
1091
|
+
gaps,
|
|
1092
|
+
parent,
|
|
1093
|
+
position: i + 1,
|
|
1094
|
+
scope: tint,
|
|
1095
|
+
segments: segments.length ? segments : undefined,
|
|
1096
|
+
size: n
|
|
1097
|
+
}, false);
|
|
1098
|
+
|
|
1099
|
+
for (let j = 0, m = segments.length; j < m; j++) {
|
|
1100
|
+
segments[j].host = row;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// An open folder whose children just arrived as one folder, as a lazy folder's can, would otherwise close
|
|
1104
|
+
// under the reader as it merges into that folder's row.
|
|
1105
|
+
if (merged && fresh && !peek(row.open!)) {
|
|
1106
|
+
write(row.open!, true);
|
|
1107
|
+
opened++;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
write(gaps, unseen);
|
|
1112
|
+
|
|
1113
|
+
return out;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// A row a rebuild found again. Showing the same element at the same depth, in the same group and tint, with the
|
|
1117
|
+
// same folders folded into it, it's kept and its rendered DOM with it, its place updated through the group's
|
|
1118
|
+
// count. Otherwise it's copied under a new key to render afresh, keeping its open state, and its folder rebuilds
|
|
1119
|
+
// at the new depth when next shown.
|
|
1120
|
+
function carry(row: Row, layout: Layout) {
|
|
1121
|
+
let element = layout.element;
|
|
1122
|
+
|
|
1123
|
+
if (row.element === element && row.depth === layout.depth && row.gaps === layout.gaps && row.scope === layout.scope && same(row.segments, layout.segments)) {
|
|
1124
|
+
row.parent = layout.parent;
|
|
1125
|
+
row.position = layout.position;
|
|
1126
|
+
row.segments = layout.segments;
|
|
1127
|
+
row.size = layout.size;
|
|
1128
|
+
|
|
1129
|
+
return row;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
let next: Row = {
|
|
1133
|
+
...row,
|
|
1134
|
+
...layout,
|
|
1135
|
+
children: null,
|
|
1136
|
+
key: ++key,
|
|
1137
|
+
locked: element.selectable === false
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
built.set(row.id, next);
|
|
1141
|
+
retarget(row, next);
|
|
1142
|
+
|
|
1143
|
+
return next;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
function change(value: string) {
|
|
1147
|
+
let edit = peek(draft)!;
|
|
1148
|
+
|
|
1149
|
+
edit.value = value;
|
|
1150
|
+
write(edit.message, inspect(edit).message);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Leaves the name input without committing; 'refocus' hands focus back to the tree, as when a key closed it.
|
|
1154
|
+
function close(refocus: boolean) {
|
|
1155
|
+
let edit = peek(draft);
|
|
1156
|
+
|
|
1157
|
+
if (!edit) {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
write(draft, null);
|
|
1162
|
+
|
|
1163
|
+
if (!edit.element) {
|
|
1164
|
+
let at = rows.indexOf(edit.row);
|
|
1165
|
+
|
|
1166
|
+
if (at !== -1) {
|
|
1167
|
+
rows.splice(at, 1);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
let back = edit.from ? rows.indexOf(edit.from) : -1;
|
|
1172
|
+
|
|
1173
|
+
move(back === -1 ? 0 : back, 'end');
|
|
1174
|
+
|
|
1175
|
+
if (split) {
|
|
1176
|
+
let rejoin = split;
|
|
1177
|
+
|
|
1178
|
+
split = null;
|
|
1179
|
+
update([rejoin]);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
if (refocus) {
|
|
1183
|
+
viewport?.focus({ preventScroll: true });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// The callback settles before the input closes; one that refuses, or fails, leaves it open to try again.
|
|
1188
|
+
function commit(refocus: boolean) {
|
|
1189
|
+
let edit = peek(draft);
|
|
1190
|
+
|
|
1191
|
+
if (!edit || peek(edit.busy)) {
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
let element = edit.element;
|
|
1196
|
+
|
|
1197
|
+
if (element && edit.value === element.name) {
|
|
1198
|
+
close(refocus);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
let { message, parent, parts } = inspect(edit);
|
|
1203
|
+
|
|
1204
|
+
if (message) {
|
|
1205
|
+
write(edit.message, message);
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
write(edit.busy, true);
|
|
1210
|
+
|
|
1211
|
+
Promise.resolve()
|
|
1212
|
+
.then(() => element ? rename!(element, edit.value) : create!(parent, parts, edit.kind))
|
|
1213
|
+
.then(
|
|
1214
|
+
(accepted) => {
|
|
1215
|
+
if (peek(draft) !== edit) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
write(edit.busy, false);
|
|
1220
|
+
|
|
1221
|
+
if (accepted !== false) {
|
|
1222
|
+
close(refocus);
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
(error) => {
|
|
1226
|
+
if (peek(draft) !== edit) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
write(edit.busy, false);
|
|
1231
|
+
write(edit.message, error instanceof Error ? error.message : String(error));
|
|
1232
|
+
}
|
|
1233
|
+
);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
function conceal(row: Row) {
|
|
1237
|
+
let at = rows.indexOf(row);
|
|
1238
|
+
|
|
1239
|
+
if (at === -1) {
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
rows.splice(at, 1 + (row.open && peek(row.open) ? visible(branch(row)).length : 0));
|
|
1244
|
+
prune();
|
|
1245
|
+
|
|
1246
|
+
if (cursor && rows.indexOf(cursor) === -1 && rows.length) {
|
|
1247
|
+
focus(rows[Math.min(at, rows.length - 1)]);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// A pinned copy keeps its name; the input for a rename lives on the row itself.
|
|
1252
|
+
function contents(row: Row, mark: Signal<Mark> | null, pinned: boolean) {
|
|
1253
|
+
let element = row.element,
|
|
1254
|
+
open = row.open;
|
|
1255
|
+
|
|
1256
|
+
return html`
|
|
1257
|
+
<span aria-hidden='true' class='file-tree-twistie'>
|
|
1258
|
+
${open && icon({ class: 'file-tree-chevron' }, remap.chevron ?? chevron)}
|
|
1259
|
+
</span>
|
|
1260
|
+
${() => !header(row) && render(element, open !== null && read(open))}
|
|
1261
|
+
${() => {
|
|
1262
|
+
let edit = read(draft);
|
|
1263
|
+
|
|
1264
|
+
if (pinned || edit?.row !== row) {
|
|
1265
|
+
return row.segments
|
|
1266
|
+
? segments(row, (node) => [
|
|
1267
|
+
{
|
|
1268
|
+
class: [
|
|
1269
|
+
() => node !== row && signal.selector(current, node.id) && '--active',
|
|
1270
|
+
search && (() => found('file-tree-segment', search.mark(node.id)))
|
|
1271
|
+
]
|
|
1272
|
+
},
|
|
1273
|
+
dragging?.segment(node) ?? {}
|
|
1274
|
+
], (node) => () => emphasis(node.element.name, read(query) || read(given)))
|
|
1275
|
+
: html`<span class='file-tree-name'>${() => emphasis(element.name, read(query) || read(given))}</span>`;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
return html`
|
|
1279
|
+
${row.segments && html`<span class='file-tree-name'>${row.segments.map((node) => `${node.element.name}/`).join('')}</span>`}
|
|
1280
|
+
${field(edit, id, {
|
|
1281
|
+
blur: leave,
|
|
1282
|
+
cancel: () => close(true),
|
|
1283
|
+
input: change,
|
|
1284
|
+
submit: () => commit(true)
|
|
1285
|
+
})}
|
|
1286
|
+
`;
|
|
1287
|
+
}}
|
|
1288
|
+
${element.symlink && icon({ 'aria-hidden': 'true', class: 'file-tree-marker' }, remap.symlink ?? symlink)}
|
|
1289
|
+
${element.readonly && icon({ 'aria-hidden': 'true', class: 'file-tree-marker' }, remap.lock ?? lock)}
|
|
1290
|
+
${mark && html`
|
|
1291
|
+
<span aria-hidden='true' class='file-tree-badge'>
|
|
1292
|
+
${() => read(mark).parts.map(chip)}
|
|
1293
|
+
</span>
|
|
1294
|
+
`}
|
|
1295
|
+
`;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// A full `git status` can reach thousands of ids under the same few folders, so each mark is refreshed once at
|
|
1299
|
+
// the end, and the rows hidden or shown are swapped in one go.
|
|
1300
|
+
function decorate(ids: Iterable<string>) {
|
|
1301
|
+
let outer = touched === null,
|
|
1302
|
+
toggled: string[] = [];
|
|
1303
|
+
|
|
1304
|
+
touched ??= new Set();
|
|
1305
|
+
|
|
1306
|
+
for (let id of ids) {
|
|
1307
|
+
let hide = unseen(id),
|
|
1308
|
+
parent = index.get(id)?.parent ?? null,
|
|
1309
|
+
// A hidden row marks nothing, its folders included; a deleted file is hidden only for being gone from
|
|
1310
|
+
// disk, and is still a change to its folders, unless it's excluded as well. A row a search filters out
|
|
1311
|
+
// is only out of view.
|
|
1312
|
+
silent = gone(id) && (filtered.has(id) || !deleted(id)),
|
|
1313
|
+
after = silent ? -1 : rank(decorations?.get(id)),
|
|
1314
|
+
before = own.get(id) ?? -1;
|
|
1315
|
+
|
|
1316
|
+
if (hide !== hidden.has(id)) {
|
|
1317
|
+
obscure(id, hide);
|
|
1318
|
+
toggled.push(id);
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
if (after !== before) {
|
|
1322
|
+
if (after === -1) {
|
|
1323
|
+
own.delete(id);
|
|
1324
|
+
}
|
|
1325
|
+
else {
|
|
1326
|
+
own.set(id, after);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
let from = lift(before),
|
|
1330
|
+
to = lift(after);
|
|
1331
|
+
|
|
1332
|
+
if (from !== to) {
|
|
1333
|
+
tally(parent, from, undefined, -1);
|
|
1334
|
+
tally(parent, to, undefined, 1);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
let decoration = silent ? undefined : decorations?.get(id),
|
|
1339
|
+
lines = [decoration?.additions ?? 0, decoration?.deletions ?? 0],
|
|
1340
|
+
previous = diffs.get(id),
|
|
1341
|
+
added = lines[0] - (previous?.[0] ?? 0),
|
|
1342
|
+
removed = lines[1] - (previous?.[1] ?? 0);
|
|
1343
|
+
|
|
1344
|
+
if (added || removed) {
|
|
1345
|
+
if (lines[0] || lines[1]) {
|
|
1346
|
+
diffs.set(id, lines);
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
diffs.delete(id);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
tally(parent, 0, [added, removed], 1);
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
refresh(id);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// One row goes or comes back on its own, unless its chain or its folder's joined or split.
|
|
1359
|
+
if (toggled.length === 1) {
|
|
1360
|
+
let id = toggled[0],
|
|
1361
|
+
groups = refold(id),
|
|
1362
|
+
row = built.get(id);
|
|
1363
|
+
|
|
1364
|
+
if (groups.length) {
|
|
1365
|
+
update(groups);
|
|
1366
|
+
}
|
|
1367
|
+
else if (row && hidden.has(id)) {
|
|
1368
|
+
conceal(row);
|
|
1369
|
+
}
|
|
1370
|
+
else if (row) {
|
|
1371
|
+
restore(row);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
else if (toggled.length) {
|
|
1375
|
+
reshow(toggled);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
if (outer) {
|
|
1379
|
+
let marked = touched;
|
|
1380
|
+
|
|
1381
|
+
touched = null;
|
|
1382
|
+
|
|
1383
|
+
for (let id of marked) {
|
|
1384
|
+
refresh(id);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
function deleted(id: string) {
|
|
1390
|
+
let decoration = decorations?.get(id);
|
|
1391
|
+
|
|
1392
|
+
return (decoration?.status ?? decoration?.staged) === 'deleted';
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// The rows below 'element' that are on screen, measured where they sit now, to stand in for rows the list is about
|
|
1396
|
+
// to swap out; places the motion layer over the viewport.
|
|
1397
|
+
function copy(element: HTMLElement, limit: number, container: HTMLElement, scroller: HTMLElement) {
|
|
1398
|
+
let edge = element.getBoundingClientRect().bottom,
|
|
1399
|
+
frame = container.getBoundingClientRect(),
|
|
1400
|
+
out: Ghost[] = [],
|
|
1401
|
+
view = scroller.getBoundingClientRect(),
|
|
1402
|
+
left = view.left + scroller.clientLeft,
|
|
1403
|
+
top = view.top + scroller.clientTop,
|
|
1404
|
+
bottom = top + scroller.clientHeight;
|
|
1405
|
+
|
|
1406
|
+
stage.edge = edge - top;
|
|
1407
|
+
stage.height = scroller.clientHeight;
|
|
1408
|
+
stage.left = left - frame.left - container.clientLeft;
|
|
1409
|
+
stage.top = top - frame.top - container.clientTop;
|
|
1410
|
+
stage.width = scroller.clientWidth;
|
|
1411
|
+
|
|
1412
|
+
for (let i = 0, node = element.nextElementSibling as Rendered | null; i < limit && node?.classList.contains('file-tree-row'); i++, node = node.nextElementSibling as Rendered | null) {
|
|
1413
|
+
let rect = node.getBoundingClientRect(),
|
|
1414
|
+
row = node[ROW];
|
|
1415
|
+
|
|
1416
|
+
if (rect.top >= bottom) {
|
|
1417
|
+
break;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
if (row) {
|
|
1421
|
+
out.push({ left: rect.left - left, row, top: rect.top - edge, width: rect.width });
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
return out;
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
// Where a paste lands: the focused folder, or the folder holding the focused file; null for the top level.
|
|
1429
|
+
function destination(row: Row) {
|
|
1430
|
+
return row.open ? row.element : (row.parent?.element ?? null);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
async function discard(elements: Element[], permanent: boolean) {
|
|
1434
|
+
if (!elements.length || (operations?.confirm && !(await operations.confirm(elements, permanent)))) {
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
operations?.delete?.(elements, permanent);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function expand(row: Row, value: boolean, inner?: Row[]) {
|
|
1442
|
+
if (!row.open || peek(row.open) === value) {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
settle();
|
|
1447
|
+
|
|
1448
|
+
let at = rows.indexOf(row);
|
|
1449
|
+
|
|
1450
|
+
if (value) {
|
|
1451
|
+
write(row.open, true);
|
|
1452
|
+
opened++;
|
|
1453
|
+
|
|
1454
|
+
if (at !== -1) {
|
|
1455
|
+
insert(at + 1, inner ?? visible(branch(row)));
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
else {
|
|
1459
|
+
if (at !== -1) {
|
|
1460
|
+
rows.splice(at + 1, (inner ?? visible(branch(row))).length);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
write(row.open, false);
|
|
1464
|
+
opened--;
|
|
1465
|
+
prune();
|
|
1466
|
+
|
|
1467
|
+
if (loader?.forget(row.id)) {
|
|
1468
|
+
row.children = null;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
if (cursor) {
|
|
1472
|
+
focus(shown(cursor));
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
ui.any = opened > 0;
|
|
1477
|
+
persist();
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
// Ctrl+click and Ctrl+Space add a row to the selection or take it out; it becomes the anchor either way.
|
|
1481
|
+
function flip(row: Row) {
|
|
1482
|
+
if (row.locked) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
anchor = row;
|
|
1487
|
+
|
|
1488
|
+
if (!selection.has(row.id)) {
|
|
1489
|
+
selection.replace([...selection.ids, row.id]);
|
|
1490
|
+
primary(row.id);
|
|
1491
|
+
// The primary it replaces may sit in a closed folder, kept only while it was primary.
|
|
1492
|
+
prune();
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
let rest = [...selection.ids].filter((id) => id !== row.id);
|
|
1497
|
+
|
|
1498
|
+
selection.replace(rest);
|
|
1499
|
+
|
|
1500
|
+
if (state.selected === row.id) {
|
|
1501
|
+
primary(rest[rest.length - 1] ?? '');
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// Opens or closes a folder the way the tree view does: an edge sweeps over its rows while the rows below ride on
|
|
1506
|
+
// it. The list swaps rows at once, so copies stand in for rows no longer rendered: the folder's own on the way
|
|
1507
|
+
// closed, and on the way open whichever rows below were pushed out of the rendered window.
|
|
1508
|
+
function fold(row: Row, value: boolean) {
|
|
1509
|
+
if (!row.open || peek(row.open) === value) {
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
settle();
|
|
1514
|
+
|
|
1515
|
+
let container = root,
|
|
1516
|
+
element = node(row),
|
|
1517
|
+
inner = visible(branch(row)),
|
|
1518
|
+
scroller = viewport;
|
|
1519
|
+
|
|
1520
|
+
if (!container || !element || !inner.length || !scroller) {
|
|
1521
|
+
expand(row, value);
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (value) {
|
|
1526
|
+
for (let i = 0, n = inner.length; i < n; i++) {
|
|
1527
|
+
revealing.set(inner[i], i);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
let measured = copy(element, value ? Infinity : inner.length, container, scroller);
|
|
1532
|
+
|
|
1533
|
+
expand(row, value, inner);
|
|
1534
|
+
|
|
1535
|
+
// Started once the list has rendered the swapped rows, ahead of their first paint.
|
|
1536
|
+
motion = requestAnimationFrame(() => {
|
|
1537
|
+
let out: Ghost[] = [];
|
|
1538
|
+
|
|
1539
|
+
// A row still rendered rides the edge itself, so the highlight on it moves with it.
|
|
1540
|
+
for (let i = 0, n = measured.length; i < n; i++) {
|
|
1541
|
+
let current = lookup(measured[i].row.id);
|
|
1542
|
+
|
|
1543
|
+
if (!current || !node(current)) {
|
|
1544
|
+
out.push(measured[i]);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
ghosts.push(...out);
|
|
1549
|
+
write(sweep, row.key);
|
|
1550
|
+
ui.motion = value ? 'open' : 'close';
|
|
1551
|
+
ui.motionRows = inner.length;
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
function focus(row: Row) {
|
|
1556
|
+
cursor = row;
|
|
1557
|
+
write(focused, row.key);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
function follow(row: Row) {
|
|
1561
|
+
clearTimeout(timer);
|
|
1562
|
+
|
|
1563
|
+
if (preview && !row.open && !row.locked) {
|
|
1564
|
+
if (preview === 'immediate') {
|
|
1565
|
+
activate(row, PREVIEW);
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
timer = setTimeout(() => activate(row, PREVIEW), REST);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// Drawn like the row it stands in for, its marks kept live; laid out by its measured box, so the depth margin
|
|
1574
|
+
// would count twice.
|
|
1575
|
+
function ghost({ left, row, top, width }: Ghost) {
|
|
1576
|
+
let style = `--depth: ${row.depth}; --guides: ${guides(row.depth)}; left: ${left}px; margin: 0; position: absolute; top: ${top}px; width: ${width}px;`;
|
|
1577
|
+
|
|
1578
|
+
if (row.notice) {
|
|
1579
|
+
return placeholder(row.notice, { style });
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
let mark = decorations ? track(row.id) : null,
|
|
1583
|
+
open = row.open;
|
|
1584
|
+
|
|
1585
|
+
return html`
|
|
1586
|
+
<div
|
|
1587
|
+
class='file-tree-row ${row.locked && '--disabled'} ${header(row) && 'file-tree-row--root'} ${() => active(row) && '--active'} ${() => holds(row, (id) => clipboard.marked.read(id)) && 'file-tree-row--cut'} ${() => holds(row, (id) => selection.read(id)) && !active(row) && 'file-tree-row--selected'}'
|
|
1588
|
+
style='${style}'
|
|
1589
|
+
${open && { 'aria-expanded': () => read(open) ? 'true' : 'false' }}
|
|
1590
|
+
${mark && { class: () => read(mark).tone && `file-tree-row--${read(mark).tone}` }}
|
|
1591
|
+
${search && { class: () => found('file-tree-row', matched(row)) }}
|
|
1592
|
+
${!!row.scope && { class: 'file-tree-row--scope', style: `--scope: ${row.scope}` }}
|
|
1593
|
+
${{ class: () => signal.selector(focused, row.key) && 'file-tree-row--focused' }}
|
|
1594
|
+
>
|
|
1595
|
+
${contents(row, mark, true)}
|
|
1596
|
+
</div>
|
|
1597
|
+
`;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
function gone(id: string) {
|
|
1601
|
+
return filtered.has(id) || (deleted(id) && !display.deleted) || (display.ignored === false && decorations?.get(id)?.status === 'ignored');
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
// The sibling group's shared count of hidden rows, kept across rebuilds so the rows already rendered go on reading
|
|
1605
|
+
// the one being written. Every write notifies, even of the same count, since a rebuild also moves kept rows to new
|
|
1606
|
+
// places the template reads alongside it.
|
|
1607
|
+
function group(elements: Element[], parent: Row | null) {
|
|
1608
|
+
let owner = parent?.id ?? null;
|
|
1609
|
+
|
|
1610
|
+
for (let i = 0, n = elements.length; i < n; i++) {
|
|
1611
|
+
let row = built.get(elements[i].id);
|
|
1612
|
+
|
|
1613
|
+
if (row && (row.parent?.id ?? null) === owner) {
|
|
1614
|
+
return row.gaps;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
return signal(0, () => false);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// A root's own row, heading its tree.
|
|
1622
|
+
function header(row: Row) {
|
|
1623
|
+
return roots && row.parent === null;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// Every row is the same height, so any row on screen measures them all.
|
|
1627
|
+
function height() {
|
|
1628
|
+
return viewport?.querySelector<HTMLElement>('.file-tree-row')?.offsetHeight ?? 0;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// Whether the path tooltip tells more than the row shows: a size or date, the folders above it, or a name cut
|
|
1632
|
+
// short. A root-level name shown in full would only be repeated.
|
|
1633
|
+
function informative(row: Row, target: HTMLElement) {
|
|
1634
|
+
let element = row.element,
|
|
1635
|
+
name = target.querySelector<HTMLElement>('.file-tree-name');
|
|
1636
|
+
|
|
1637
|
+
if (element.size !== undefined || element.modified !== undefined || !name) {
|
|
1638
|
+
return true;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
return name.scrollWidth > name.clientWidth || path(row) !== name.textContent?.trim();
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
function insert(at: number, items: Row[]) {
|
|
1645
|
+
for (let i = 0, n = items.length; i < n; i += CHUNK) {
|
|
1646
|
+
rows.splice(at + i, 0, ...items.slice(i, i + CHUNK));
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function inspect(edit: Draft<Row>) {
|
|
1651
|
+
let parent = edit.parent?.element ?? null,
|
|
1652
|
+
result = resolve(edit.value, parent, edit.element, siblings, folder);
|
|
1653
|
+
|
|
1654
|
+
if (!result.message && validate) {
|
|
1655
|
+
result.message = validate(edit.value, parent, edit.element) || '';
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
return result;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// The folder 'element' shares its row with, when compacting. A hidden folder has no row to share, the one split
|
|
1662
|
+
// out for a name input keeps its own, and a root heads its tree alone, though a chain may start below it.
|
|
1663
|
+
function joined(element: Element) {
|
|
1664
|
+
if (!compact || element.id === split || hidden.has(element.id) || !folder(element) || (roots && index.get(element.id)?.parent === null)) {
|
|
1665
|
+
return null;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
return sole(element, hidden, folder);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
// Moves the cursor to the next or previous file 'kind' marks, in tree order, opening the folders down to it.
|
|
1672
|
+
async function jump(kind: Jump, step: 1 | -1) {
|
|
1673
|
+
if (!decorations) {
|
|
1674
|
+
return null;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
if (loader) {
|
|
1678
|
+
await uncover(kind);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
let found: { place: number[]; row: Row }[] = [];
|
|
1682
|
+
|
|
1683
|
+
for (let id of decorations.keys()) {
|
|
1684
|
+
let entry = index.get(id);
|
|
1685
|
+
|
|
1686
|
+
if (!entry || folder(entry.element) || hidden.has(id) || !flagged(decorations.get(id), kind)) {
|
|
1687
|
+
continue;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
let row = locate(id)!;
|
|
1691
|
+
|
|
1692
|
+
found.push({ place: place(row), row });
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
found.sort((a, b) => order(a.place, b.place));
|
|
1696
|
+
|
|
1697
|
+
let at = seek(found.map((item) => item.place), cursor ? place(cursor) : null, step);
|
|
1698
|
+
|
|
1699
|
+
if (at === -1) {
|
|
1700
|
+
return null;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
let target = found[at].row;
|
|
1704
|
+
|
|
1705
|
+
reveal(target.id, true);
|
|
1706
|
+
follow(target);
|
|
1707
|
+
|
|
1708
|
+
return target.element;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// The row for 'layout', the one built before for its element when there is one. A folder folded into a compact
|
|
1712
|
+
// row reads as open but counts among the open folders only while it has a row of its own; folding in or out, its
|
|
1713
|
+
// children, built for where it stood, rebuild when it next shows them.
|
|
1714
|
+
function lay(layout: Layout, folded: boolean) {
|
|
1715
|
+
let element = layout.element,
|
|
1716
|
+
previous = built.get(element.id);
|
|
1717
|
+
|
|
1718
|
+
if (!previous) {
|
|
1719
|
+
let open = folder(element) ? folded || initial.has(element.id) : null,
|
|
1720
|
+
row: Row = {
|
|
1721
|
+
...layout,
|
|
1722
|
+
children: null,
|
|
1723
|
+
id: element.id,
|
|
1724
|
+
key: ++key,
|
|
1725
|
+
locked: element.selectable === false,
|
|
1726
|
+
open: open === null ? null : signal(open)
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
if (open && !folded) {
|
|
1730
|
+
opened++;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
built.set(element.id, row);
|
|
1734
|
+
|
|
1735
|
+
return row;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
let was = !!previous.host,
|
|
1739
|
+
row = carry(previous, layout);
|
|
1740
|
+
|
|
1741
|
+
// Set again by the build that folds it.
|
|
1742
|
+
row.host = undefined;
|
|
1743
|
+
|
|
1744
|
+
if (was !== folded) {
|
|
1745
|
+
row.children = null;
|
|
1746
|
+
|
|
1747
|
+
// Unfolded, it counts only while open: a search ending shuts one it opened, folded or not.
|
|
1748
|
+
if (!folded) {
|
|
1749
|
+
opened += peek(row.open!) ? 1 : 0;
|
|
1750
|
+
}
|
|
1751
|
+
else if (peek(row.open!)) {
|
|
1752
|
+
opened--;
|
|
1753
|
+
}
|
|
1754
|
+
else {
|
|
1755
|
+
write(row.open!, true);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// A compact row's spoken name reads its folders' names, which the store may have just changed.
|
|
1760
|
+
if (row !== previous) {
|
|
1761
|
+
refresh(element.id);
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
return row;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// Clicking away commits a good name and drops a bad one, as VS Code does.
|
|
1768
|
+
function leave() {
|
|
1769
|
+
let edit = peek(draft);
|
|
1770
|
+
|
|
1771
|
+
if (!edit || peek(edit.busy)) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
if (peek(edit.message) || inspect(edit).message) {
|
|
1776
|
+
close(false);
|
|
1777
|
+
}
|
|
1778
|
+
else {
|
|
1779
|
+
commit(false);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// Loaded children join the store, reaching every tree showing it; a failure shows on this tree alone. A folder
|
|
1784
|
+
// removed meanwhile drops them, and one another tree already filled keeps what it has.
|
|
1785
|
+
function loaded(id: string, children: Element[] | null) {
|
|
1786
|
+
let element = model.get(id);
|
|
1787
|
+
|
|
1788
|
+
if (!element) {
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (children && !element.children) {
|
|
1793
|
+
model.add(children, id);
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
update([id]);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// The row for 'id', building the folders above it without opening them.
|
|
1801
|
+
function locate(id: string) {
|
|
1802
|
+
let chain: string[] = [];
|
|
1803
|
+
|
|
1804
|
+
for (let node = index.get(id)?.parent ?? null; node !== null; node = index.get(node)!.parent) {
|
|
1805
|
+
chain.push(node);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// A folder folded into a compact row lists nothing itself; the row's last folder lists what it shows.
|
|
1809
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
1810
|
+
let row = built.get(chain[i])!;
|
|
1811
|
+
|
|
1812
|
+
if (!row.host) {
|
|
1813
|
+
branch(row);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
let row = built.get(id);
|
|
1818
|
+
|
|
1819
|
+
return row?.host ?? row;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
function look(id: string): Mark {
|
|
1823
|
+
let entry = index.get(id),
|
|
1824
|
+
inside = -1,
|
|
1825
|
+
list = counts.get(id);
|
|
1826
|
+
|
|
1827
|
+
if (list) {
|
|
1828
|
+
for (let i = list.length - 1; i > 0; i--) {
|
|
1829
|
+
if (list[i]) {
|
|
1830
|
+
inside = i;
|
|
1831
|
+
break;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
let decoration = decorations?.get(id),
|
|
1837
|
+
highest = Math.max(own.get(id) ?? -1, inside),
|
|
1838
|
+
mine = diffs.get(id),
|
|
1839
|
+
total = totals.get(id),
|
|
1840
|
+
lines = display.stats ? [(mine?.[0] ?? 0) + (total?.[0] ?? 0), (mine?.[1] ?? 0) + (total?.[1] ?? 0)] : [0, 0],
|
|
1841
|
+
row = built.get(id),
|
|
1842
|
+
subject = entry?.element ?? { name: id },
|
|
1843
|
+
label = spoken(
|
|
1844
|
+
{ ...subject, name: row?.segments ? caption(row) : subject.name, root: roots && entry?.parent === null },
|
|
1845
|
+
decoration,
|
|
1846
|
+
lines,
|
|
1847
|
+
inside > 0 ? RANK[inside] : undefined
|
|
1848
|
+
),
|
|
1849
|
+
parts = badge(decoration, lines, entry && folder(entry.element) ? (highest > 0 ? RANK[highest] : '') : null),
|
|
1850
|
+
tone: Mark['tone'] = highest === -1 ? '' : RANK[highest];
|
|
1851
|
+
|
|
1852
|
+
return { key: JSON.stringify([label, parts]), label, parts, tone };
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// A notice row isn't among the built rows; a drag over one aims at its folder.
|
|
1856
|
+
function lookup(id: string) {
|
|
1857
|
+
return built.get(id) ?? rows.find((row) => row.notice && row.id === id);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// The search's mark for a row, from any of its folders: the current match wherever it sits in a compact row.
|
|
1861
|
+
function matched(row: Row) {
|
|
1862
|
+
let out: string | false = false;
|
|
1863
|
+
|
|
1864
|
+
for (let node of lineage(row)) {
|
|
1865
|
+
let mark = search!.mark(node.id);
|
|
1866
|
+
|
|
1867
|
+
if (mark === 'current') {
|
|
1868
|
+
return mark;
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
out ||= mark;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
return out;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
function modes() {
|
|
1878
|
+
return [display.deleted, display.dotfiles, display.ignored, display.stats].join();
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
function move(index: number, align: Align) {
|
|
1882
|
+
let row = rows[index];
|
|
1883
|
+
|
|
1884
|
+
if (!row || !viewport) {
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
focus(row);
|
|
1889
|
+
|
|
1890
|
+
let element = node(row);
|
|
1891
|
+
|
|
1892
|
+
// Outside the rendered window; the slot scrolls it in and renders it straight away. Aligned to the top, it
|
|
1893
|
+
// starts the view at the rows its pinned folders will cover, one each.
|
|
1894
|
+
if (!element) {
|
|
1895
|
+
slot.scrollTo(align === 'start' ? index - pins.cover(row) : index, align);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
let r = element.getBoundingClientRect(),
|
|
1900
|
+
v = viewport.getBoundingClientRect(),
|
|
1901
|
+
top = v.top + pins.cover(row) * r.height;
|
|
1902
|
+
|
|
1903
|
+
if (r.top < top) {
|
|
1904
|
+
viewport.scrollTop -= top - r.top;
|
|
1905
|
+
}
|
|
1906
|
+
else if (r.bottom > v.bottom) {
|
|
1907
|
+
viewport.scrollTop += r.bottom - v.bottom;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
function notice(parent: Row, value: Notice): Row {
|
|
1912
|
+
let at = ++key,
|
|
1913
|
+
self = `${id}-${at}`;
|
|
1914
|
+
|
|
1915
|
+
return {
|
|
1916
|
+
children: null,
|
|
1917
|
+
depth: parent.depth + 1,
|
|
1918
|
+
// Nameless, so type-ahead passes over it.
|
|
1919
|
+
element: { id: self, name: '' },
|
|
1920
|
+
gaps: signal(0),
|
|
1921
|
+
id: self,
|
|
1922
|
+
key: at,
|
|
1923
|
+
locked: true,
|
|
1924
|
+
notice: value,
|
|
1925
|
+
open: null,
|
|
1926
|
+
parent,
|
|
1927
|
+
position: 1,
|
|
1928
|
+
scope: parent.scope,
|
|
1929
|
+
size: 1
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
// A row's element while it is rendered; the rest are virtualized away. Found by id, so a row rendered by a flush
|
|
1934
|
+
// earlier in this task is found before its onconnect has run.
|
|
1935
|
+
function node(row: Row) {
|
|
1936
|
+
return document.getElementById(`${id}-${row.key}`);
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// Every row hidden or shown goes through here: its group's count moves ahead of any rebuild, which counts the
|
|
1940
|
+
// group afresh, and the places counted from what was hidden are counted again.
|
|
1941
|
+
function obscure(id: string, value: boolean) {
|
|
1942
|
+
let row = built.get(id);
|
|
1943
|
+
|
|
1944
|
+
if (value) {
|
|
1945
|
+
hidden.add(id);
|
|
1946
|
+
}
|
|
1947
|
+
else {
|
|
1948
|
+
hidden.delete(id);
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
shifts++;
|
|
1952
|
+
|
|
1953
|
+
if (row) {
|
|
1954
|
+
write(row.gaps, peek(row.gaps) + (value ? 1 : -1));
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
function onscreen(row: Row): Attributes {
|
|
1959
|
+
return {
|
|
1960
|
+
onconnect: (element: Rendered) => {
|
|
1961
|
+
element[ROW] = row;
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
// The row 'element' draws, among the built ones: notices stand in for rows and pinned copies draw none.
|
|
1967
|
+
function owner(element: Rendered | null) {
|
|
1968
|
+
let row = element?.[ROW];
|
|
1969
|
+
|
|
1970
|
+
return row && !row.notice ? row : null;
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
function page() {
|
|
1974
|
+
let size = height();
|
|
1975
|
+
|
|
1976
|
+
return size && viewport ? Math.max(1, Math.floor(viewport.clientHeight / size)) : 1;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
// False when the tree has nothing to do with the command, leaving the key to the page.
|
|
1980
|
+
function perform(name: Command, row: Row) {
|
|
1981
|
+
switch (name) {
|
|
1982
|
+
case 'all': {
|
|
1983
|
+
let ids: string[] = [];
|
|
1984
|
+
|
|
1985
|
+
for (let i = 0, n = rows.length; i < n; i++) {
|
|
1986
|
+
if (!rows[i].locked) {
|
|
1987
|
+
ids.push(rows[i].id);
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
selection.replace(ids);
|
|
1992
|
+
|
|
1993
|
+
if (!selection.has(state.selected) && !row.locked) {
|
|
1994
|
+
primary(row.id);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
return true;
|
|
1998
|
+
}
|
|
1999
|
+
case 'clear': {
|
|
2000
|
+
// Down to the focused row, or the primary when the focused row can't be selected. With nothing to
|
|
2001
|
+
// clear, Escape is left to whatever holds the tree, like a dialog closing.
|
|
2002
|
+
let cut = clipboard.cancel(),
|
|
2003
|
+
lead = row.locked ? built.get(state.selected) : row;
|
|
2004
|
+
|
|
2005
|
+
if (!cut && selection.ids.size < 2) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
selection.replace(lead ? [lead.id] : []);
|
|
2010
|
+
|
|
2011
|
+
if (lead) {
|
|
2012
|
+
anchor = lead;
|
|
2013
|
+
primary(lead.id);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
return true;
|
|
2017
|
+
}
|
|
2018
|
+
case 'copy':
|
|
2019
|
+
case 'cut': {
|
|
2020
|
+
let elements = targets(row, true);
|
|
2021
|
+
|
|
2022
|
+
if (!operations || !elements.length) {
|
|
2023
|
+
return false;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
clipboard.hold(elements, name === 'cut');
|
|
2027
|
+
operations[name]?.(elements);
|
|
2028
|
+
return true;
|
|
2029
|
+
}
|
|
2030
|
+
case 'delete':
|
|
2031
|
+
case 'trash':
|
|
2032
|
+
if (!operations?.delete) {
|
|
2033
|
+
return false;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
void discard(targets(row, true), name === 'delete');
|
|
2037
|
+
return true;
|
|
2038
|
+
case 'duplicate': {
|
|
2039
|
+
let elements = targets(row, true);
|
|
2040
|
+
|
|
2041
|
+
if (!operations?.duplicate || !elements.length) {
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
2045
|
+
operations.duplicate(elements);
|
|
2046
|
+
return true;
|
|
2047
|
+
}
|
|
2048
|
+
case 'paste': {
|
|
2049
|
+
let paste = operations?.paste,
|
|
2050
|
+
target = destination(segment(row));
|
|
2051
|
+
|
|
2052
|
+
if (!paste) {
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
void clipboard.paste((elements, cut) => paste(elements, target, cut));
|
|
2057
|
+
return true;
|
|
2058
|
+
}
|
|
2059
|
+
case 'path':
|
|
2060
|
+
case 'relative': {
|
|
2061
|
+
let elements = targets(row, false);
|
|
2062
|
+
|
|
2063
|
+
if (!elements.length) {
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
void writeText(elements.map((element) => address(element, name === 'relative')).join('\n'));
|
|
2068
|
+
return true;
|
|
2069
|
+
}
|
|
2070
|
+
case 'redo':
|
|
2071
|
+
case 'undo':
|
|
2072
|
+
if (!history || !(name === 'undo' ? history.canUndo : history.canRedo)) {
|
|
2073
|
+
return false;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
void history[name]();
|
|
2077
|
+
return true;
|
|
2078
|
+
case 'toggle':
|
|
2079
|
+
flip(row);
|
|
2080
|
+
return true;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// The folders open here, and those still to open once they're shown or loaded.
|
|
2085
|
+
function persist() {
|
|
2086
|
+
if (!snapshot) {
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
let out: string[] = [];
|
|
2091
|
+
|
|
2092
|
+
for (let [id, row] of built) {
|
|
2093
|
+
if (row.open && peek(row.open) && !unfolded.get(id)) {
|
|
2094
|
+
out.push(id);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
for (let id of initial) {
|
|
2099
|
+
if (!built.has(id) && (loader || index.has(id))) {
|
|
2100
|
+
out.push(id);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
snapshot.expanded = out;
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
// Counted among the siblings still shown, so hidden rows leave no hole in "3 of 5".
|
|
2108
|
+
function position(row: Row) {
|
|
2109
|
+
// A row still rendered as its folder folds into a compact row has no list left; it's on its way out.
|
|
2110
|
+
let siblings = row.parent ? row.parent.children : top;
|
|
2111
|
+
|
|
2112
|
+
if (!read(row.gaps) || !siblings) {
|
|
2113
|
+
return row.position;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
let counted = places.get(siblings);
|
|
2117
|
+
|
|
2118
|
+
if (!counted || counted.at !== shifts) {
|
|
2119
|
+
let before = new Int32Array(siblings.length + 1);
|
|
2120
|
+
|
|
2121
|
+
for (let i = 0, n = siblings.length; i < n; i++) {
|
|
2122
|
+
before[i + 1] = before[i] + +hidden.has(siblings[i].id);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
places.set(siblings, counted = { at: shifts, before });
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
return row.position - counted.before[row.position - 1];
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
// A click previews a file and a double or middle click pins it, beside the current one with Alt. Alt+click opens
|
|
2132
|
+
// or closes a folder with everything in it. Shift+click selects a range and Ctrl/Cmd+click toggles one row.
|
|
2133
|
+
function press(row: Row, event: MouseEvent) {
|
|
2134
|
+
let from = cursor,
|
|
2135
|
+
subject = pick(row, event),
|
|
2136
|
+
type = event.type;
|
|
2137
|
+
|
|
2138
|
+
// Right clicks belong to the context menu.
|
|
2139
|
+
if (type === 'auxclick' && event.button !== 1) {
|
|
2140
|
+
return;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
2143
|
+
focus(row);
|
|
2144
|
+
|
|
2145
|
+
if (type === 'click' && event.shiftKey) {
|
|
2146
|
+
span(from ?? row, row);
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
if (type === 'click' && (event.ctrlKey || event.metaKey)) {
|
|
2151
|
+
flip(subject);
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
if (!row.open) {
|
|
2156
|
+
activate(row, { mode: type === 'click' ? 'preview' : 'pinned', side: event.altKey });
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
// Whether a single click opens it; a double click does whenever that doesn't.
|
|
2161
|
+
let single = unfold === 'click' || twistie(event);
|
|
2162
|
+
|
|
2163
|
+
if (type === 'click' && event.altKey) {
|
|
2164
|
+
activate(row, PREVIEW, false, subject);
|
|
2165
|
+
all(!peek(row.open), row);
|
|
2166
|
+
}
|
|
2167
|
+
else if (type === 'click') {
|
|
2168
|
+
activate(row, PREVIEW, single, subject);
|
|
2169
|
+
}
|
|
2170
|
+
else if (type === 'dblclick' && !single && !event.altKey && !row.locked) {
|
|
2171
|
+
fold(row, !peek(row.open));
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
function primary(id: string) {
|
|
2176
|
+
quiet = id;
|
|
2177
|
+
state.selected = id;
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// Rows hidden as their folder closes leave the selection, as in VS Code. The primary stays: it's the file the
|
|
2181
|
+
// editor shows, and the highlight finds it again once the folder reopens.
|
|
2182
|
+
function prune() {
|
|
2183
|
+
selection.replace([...selection.ids].filter((id) => {
|
|
2184
|
+
let row = built.get(id);
|
|
2185
|
+
|
|
2186
|
+
return id === state.selected || (!!row && !hidden.has(id) && shown(row) === row);
|
|
2187
|
+
}));
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
function purge(ids: string[]) {
|
|
2191
|
+
for (let i = 0, n = ids.length; i < n; i++) {
|
|
2192
|
+
let id = ids[i],
|
|
2193
|
+
row = built.get(id);
|
|
2194
|
+
|
|
2195
|
+
if (row?.open && peek(row.open) && !row.host) {
|
|
2196
|
+
opened--;
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
if (anchor?.id === id) {
|
|
2200
|
+
anchor = null;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
built.delete(id);
|
|
2204
|
+
counts.delete(id);
|
|
2205
|
+
diffs.delete(id);
|
|
2206
|
+
filtered.delete(id);
|
|
2207
|
+
hidden.delete(id);
|
|
2208
|
+
initial.delete(id);
|
|
2209
|
+
marks.delete(id);
|
|
2210
|
+
notes.delete(id);
|
|
2211
|
+
own.delete(id);
|
|
2212
|
+
totals.delete(id);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// Keeps the cursor on its row through a change: on the folder it went into when that's closed, or where it stood
|
|
2217
|
+
// once the row is gone.
|
|
2218
|
+
function recover(at: number) {
|
|
2219
|
+
if (cursor && rows.includes(cursor)) {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
let row: Row | undefined;
|
|
2224
|
+
|
|
2225
|
+
for (let node = cursor && index.has(cursor.id) ? cursor.id : null; node !== null; node = index.get(node)!.parent) {
|
|
2226
|
+
let found = built.get(node),
|
|
2227
|
+
candidate = found?.host ?? found;
|
|
2228
|
+
|
|
2229
|
+
if (candidate && rows.includes(candidate)) {
|
|
2230
|
+
row = candidate;
|
|
2231
|
+
break;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
if (row) {
|
|
2236
|
+
focus(row);
|
|
2237
|
+
}
|
|
2238
|
+
else if (rows.length) {
|
|
2239
|
+
focus(rows[Math.min(Math.max(at, 0), rows.length - 1)]);
|
|
2240
|
+
}
|
|
2241
|
+
else {
|
|
2242
|
+
cursor = null;
|
|
2243
|
+
write(focused, 0);
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// 'force' rebuilds the rows even when none was hidden or shown, as when folders opened or closed. What the
|
|
2248
|
+
// patterns hide is left as it is: only the display settings change it, and a search redisplays on every key.
|
|
2249
|
+
function redisplay(force = false) {
|
|
2250
|
+
settle();
|
|
2251
|
+
|
|
2252
|
+
let changed: string[] = [];
|
|
2253
|
+
|
|
2254
|
+
for (let id of index.keys()) {
|
|
2255
|
+
rehide(id, changed);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
for (let id of decorations?.keys() ?? []) {
|
|
2259
|
+
if (!index.has(id)) {
|
|
2260
|
+
rehide(id, changed);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
// Hidden is already in step, so this only moves the folder marks; the rows are swapped below in one go.
|
|
2265
|
+
decorate(changed);
|
|
2266
|
+
|
|
2267
|
+
for (let id of marks.keys()) {
|
|
2268
|
+
refresh(id);
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
if (changed.length || force) {
|
|
2272
|
+
reshow(changed);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
// Chains count only what's shown, so hiding or showing 'id' can join or split its own and its folder's: the
|
|
2277
|
+
// folders to rebuild for it, each listing a row that no longer compacts as it would now.
|
|
2278
|
+
function refold(id: string) {
|
|
2279
|
+
let out: (string | null)[] = [];
|
|
2280
|
+
|
|
2281
|
+
for (let node of [id, index.get(id)?.parent ?? null]) {
|
|
2282
|
+
let row = node === null ? undefined : built.get(node);
|
|
2283
|
+
|
|
2284
|
+
if (row && stale(row)) {
|
|
2285
|
+
out.push((row.host ?? row).parent?.id ?? null);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
return out;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
function refresh(id: string) {
|
|
2293
|
+
let mark = marks.get(id);
|
|
2294
|
+
|
|
2295
|
+
if (!mark) {
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
if (touched) {
|
|
2300
|
+
touched.add(id);
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
let current = peek(mark),
|
|
2305
|
+
next = look(id);
|
|
2306
|
+
|
|
2307
|
+
if (current.key !== next.key || current.tone !== next.tone) {
|
|
2308
|
+
write(mark, next);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
// The folder to rebuild for a change inside 'id'. Folded into a compact row, or folding differently now, its rows
|
|
2313
|
+
// are the folder's listing that row.
|
|
2314
|
+
function regroup(id: string | null) {
|
|
2315
|
+
let row = id === null ? undefined : built.get(id);
|
|
2316
|
+
|
|
2317
|
+
return row && (row.host || stale(row)) ? (row.host ?? row).parent?.id ?? null : id;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
function rehide(id: string, changed: string[]) {
|
|
2321
|
+
let hide = unseen(id);
|
|
2322
|
+
|
|
2323
|
+
if (hide !== hidden.has(id)) {
|
|
2324
|
+
obscure(id, hide);
|
|
2325
|
+
changed.push(id);
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
// Swaps in the rows once 'changed' were hidden or shown, many at a time: one pass over the rows rather than a
|
|
2330
|
+
// search and a splice for each.
|
|
2331
|
+
function reshow(changed: string[]) {
|
|
2332
|
+
settle();
|
|
2333
|
+
|
|
2334
|
+
// Chains count only what's shown, so those the change joined or split are rebuilt before the rows are swapped.
|
|
2335
|
+
for (let group of new Set(changed.flatMap(refold))) {
|
|
2336
|
+
sync(group);
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
if (cursor?.host) {
|
|
2340
|
+
focus(cursor.host);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
let next = visible(top),
|
|
2344
|
+
kept = new Set(next),
|
|
2345
|
+
// A hidden cursor moves to the first row after it still shown, as it does when a single row goes.
|
|
2346
|
+
target = cursor && !kept.has(cursor)
|
|
2347
|
+
? rows.slice(rows.indexOf(cursor)).find((row) => kept.has(row)) ?? next[next.length - 1]
|
|
2348
|
+
: undefined;
|
|
2349
|
+
|
|
2350
|
+
swap(0, rows.length, next);
|
|
2351
|
+
prune();
|
|
2352
|
+
|
|
2353
|
+
if (target) {
|
|
2354
|
+
focus(target);
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
ui.any = opened > 0;
|
|
2358
|
+
persist();
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
// Puts a row back after its previous visible sibling's subtree, when its folder is open and on screen.
|
|
2362
|
+
function restore(row: Row) {
|
|
2363
|
+
let parent = row.parent,
|
|
2364
|
+
at = parent ? rows.indexOf(parent) + 1 : 0;
|
|
2365
|
+
|
|
2366
|
+
if (parent && (!peek(parent.open!) || at === 0)) {
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
let siblings = parent ? branch(parent) : top;
|
|
2371
|
+
|
|
2372
|
+
for (let i = row.position - 2; i >= 0; i--) {
|
|
2373
|
+
let sibling = siblings[i];
|
|
2374
|
+
|
|
2375
|
+
if (!hidden.has(sibling.id)) {
|
|
2376
|
+
at = rows.indexOf(sibling) + 1 + (sibling.open && peek(sibling.open) ? visible(branch(sibling)).length : 0);
|
|
2377
|
+
break;
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
insert(at, [row, ...(row.open && peek(row.open) ? visible(branch(row)) : [])]);
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
// A change from the store, applied to the folders it touched.
|
|
2385
|
+
function restructure({ elements, from = null, ids, parent, type }: Change) {
|
|
2386
|
+
let groups: (string | null)[] = [];
|
|
2387
|
+
|
|
2388
|
+
seen = model.version;
|
|
2389
|
+
|
|
2390
|
+
if (type === 'remove') {
|
|
2391
|
+
let edit = peek(draft),
|
|
2392
|
+
removed = new Set(ids);
|
|
2393
|
+
|
|
2394
|
+
if (edit && ((edit.element && removed.has(edit.element.id)) || (edit.parent && removed.has(edit.parent.id)))) {
|
|
2395
|
+
close(false);
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
shift(ids, parent, null);
|
|
2399
|
+
purge(ids);
|
|
2400
|
+
|
|
2401
|
+
// The rest of the selection takes over from a removed primary.
|
|
2402
|
+
if (removed.has(state.selected)) {
|
|
2403
|
+
primary([...selection.ids].find((id) => !removed.has(id)) ?? '');
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
else {
|
|
2407
|
+
// Exclude and dotfile patterns match paths, so whatever was added, moved or renamed is checked again,
|
|
2408
|
+
// before the rows are built so a new row that's hidden is never inserted.
|
|
2409
|
+
for (let id of ids) {
|
|
2410
|
+
filtered.delete(id);
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
for (let i = 0, n = elements.length; i < n; i++) {
|
|
2414
|
+
recheck(elements[i], index, display.dotfiles !== false, excluded, roots, filtered);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
for (let id of ids) {
|
|
2418
|
+
// A root added live opens, as those given at first do.
|
|
2419
|
+
if (roots && type === 'add' && index.get(id)?.parent === null) {
|
|
2420
|
+
initial.add(id);
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
if (unseen(id) === hidden.has(id)) {
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
obscure(id, !hidden.has(id));
|
|
2428
|
+
// Deeper in, a folder kept as it was can still hold a chain the change joined or split.
|
|
2429
|
+
groups.push(...refold(id));
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
if (type === 'move') {
|
|
2433
|
+
shift(ids, from, parent);
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
// Hidden is already in step, so this only moves the folder marks.
|
|
2437
|
+
if (decorations) {
|
|
2438
|
+
decorate(ids);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
update([...(type === 'move' && from !== parent ? [from, parent] : [parent]), ...groups]);
|
|
2443
|
+
search?.refresh();
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// Catches up on changes made while disconnected, which went unseen: every folder rebuilds when next shown.
|
|
2447
|
+
function resync() {
|
|
2448
|
+
close(false);
|
|
2449
|
+
|
|
2450
|
+
for (let [id, row] of built) {
|
|
2451
|
+
if (index.has(id)) {
|
|
2452
|
+
row.children = null;
|
|
2453
|
+
}
|
|
2454
|
+
else {
|
|
2455
|
+
purge([id]);
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
filtered = filter(model.elements, display.dotfiles !== false, excluded, roots);
|
|
2460
|
+
hidden = new Set([...filtered, ...(decorations?.keys() ?? [])].filter(gone));
|
|
2461
|
+
shifts++;
|
|
2462
|
+
|
|
2463
|
+
// Rolled up again from scratch by the decorations catching up on connect.
|
|
2464
|
+
if (decorations) {
|
|
2465
|
+
counts.clear();
|
|
2466
|
+
diffs.clear();
|
|
2467
|
+
own.clear();
|
|
2468
|
+
totals.clear();
|
|
2469
|
+
|
|
2470
|
+
for (let id of marks.keys()) {
|
|
2471
|
+
refresh(id);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
|
|
2475
|
+
seen = model.version;
|
|
2476
|
+
update([null]);
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
// Points whatever held a row at the copy replacing it.
|
|
2480
|
+
function retarget(row: Row, next: Row) {
|
|
2481
|
+
let edit = peek(draft);
|
|
2482
|
+
|
|
2483
|
+
if (anchor === row) {
|
|
2484
|
+
anchor = next;
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
if (cursor === row) {
|
|
2488
|
+
focus(next);
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
if (!edit) {
|
|
2492
|
+
return;
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
if (edit.from === row) {
|
|
2496
|
+
edit.from = next;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
if (edit.parent === row) {
|
|
2500
|
+
edit.parent = next;
|
|
2501
|
+
|
|
2502
|
+
if (!edit.element) {
|
|
2503
|
+
edit.row.parent = next;
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
if (edit.row === row) {
|
|
2508
|
+
edit.element = next.element;
|
|
2509
|
+
edit.row = next;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
// Selects what an undo or redo brought back, moved or renamed, in tree order, revealing the first; one that only
|
|
2514
|
+
// took items away selects the folder they left.
|
|
2515
|
+
function retrace({ operations }: Step) {
|
|
2516
|
+
let ids = new Set<string>(),
|
|
2517
|
+
left: string | null = null;
|
|
2518
|
+
|
|
2519
|
+
for (let i = 0, n = operations.length; i < n; i++) {
|
|
2520
|
+
let operation = operations[i];
|
|
2521
|
+
|
|
2522
|
+
if (operation.type === 'remove') {
|
|
2523
|
+
left ??= operation.parent;
|
|
2524
|
+
}
|
|
2525
|
+
else {
|
|
2526
|
+
ids.add(operation.element.id);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
if (!ids.size && left !== null) {
|
|
2531
|
+
ids.add(left);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
// By id, since a folder folded into a compact row is selected as its segment, on the row that shows it.
|
|
2535
|
+
let found: { id: string; place: number[]; row: Row }[] = [];
|
|
2536
|
+
|
|
2537
|
+
for (let id of ids) {
|
|
2538
|
+
let row = locate(id);
|
|
2539
|
+
|
|
2540
|
+
if (row && !row.locked && !hidden.has(id)) {
|
|
2541
|
+
found.push({ id, place: place(row), row });
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
if (!found.length) {
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
found.sort((a, b) => order(a.place, b.place));
|
|
2550
|
+
anchor = found[0].row;
|
|
2551
|
+
selection.replace(found.map((item) => item.id));
|
|
2552
|
+
primary(found[0].id);
|
|
2553
|
+
reveal(found[0].id, true);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
// The row under a folder that failed to load tries again; the loading row does nothing.
|
|
2557
|
+
function retry(row: Row) {
|
|
2558
|
+
if (row.notice?.type === 'error' && loader?.forget(row.parent!.id)) {
|
|
2559
|
+
update([row.parent!.id]);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
function reveal(id: string, scroll: boolean) {
|
|
2564
|
+
let row = locate(id);
|
|
2565
|
+
|
|
2566
|
+
if (!row) {
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
for (let node = row.parent; node; node = node.parent) {
|
|
2571
|
+
expand(node, true);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
let at = rows.indexOf(row);
|
|
2575
|
+
|
|
2576
|
+
if (at === -1) {
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
if (scroll) {
|
|
2581
|
+
move(at, 'center');
|
|
2582
|
+
}
|
|
2583
|
+
else {
|
|
2584
|
+
focus(row);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2588
|
+
// 'full' is the element's path from the tree's root, or from its own root under 'roots'; 'inherited' its
|
|
2589
|
+
// folder's tint.
|
|
2590
|
+
function scope(element: Element, full: string, inherited: string) {
|
|
2591
|
+
for (let i = 0, n = scopes.length; i < n; i++) {
|
|
2592
|
+
let match = scopes[i].match;
|
|
2593
|
+
|
|
2594
|
+
if (typeof match === 'function' ? match(element) : match.test(full)) {
|
|
2595
|
+
return scopes[i].color;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
return inherited;
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
// What a command on 'row' acts on: the folder of a compact row's selected segment, or the row's own.
|
|
2603
|
+
function segment(row: Row) {
|
|
2604
|
+
return row.segments?.find((node) => node.id === state.selected) ?? row;
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// Splits a compact row at 'row', which shows as the last folder of what's left of it; the chain joins again once
|
|
2608
|
+
// the name input closes.
|
|
2609
|
+
function separate(row: Row) {
|
|
2610
|
+
split = row.id;
|
|
2611
|
+
update([row.id]);
|
|
2612
|
+
|
|
2613
|
+
return built.get(row.id)!;
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
function settle() {
|
|
2617
|
+
if (!motion) {
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
cancelAnimationFrame(motion);
|
|
2622
|
+
motion = 0;
|
|
2623
|
+
ghosts.splice(0, ghosts.length);
|
|
2624
|
+
revealing.clear();
|
|
2625
|
+
write(sweep, 0);
|
|
2626
|
+
ui.motion = '';
|
|
2627
|
+
// Off now, before a fold that follows measures its rows, so its own motion restarts the animation.
|
|
2628
|
+
flush();
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
// Moves the rollup that 'ids' carry, their tones and their lines, from one chain of folders to another; null ends
|
|
2632
|
+
// a chain.
|
|
2633
|
+
function shift(ids: string[], from: string | null, to: string | null) {
|
|
2634
|
+
for (let i = 0, n = ids.length; i < n; i++) {
|
|
2635
|
+
let lines = diffs.get(ids[i]),
|
|
2636
|
+
value = lift(own.get(ids[i]) ?? -1);
|
|
2637
|
+
|
|
2638
|
+
if (value > 0 || lines) {
|
|
2639
|
+
tally(from, value, lines, -1);
|
|
2640
|
+
tally(to, value, lines, 1);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
// The names a new or renamed item can clash with. Deleted files are gone from disk, even shown, so theirs are free.
|
|
2646
|
+
function siblings(parent: Element | null) {
|
|
2647
|
+
return (parent ? parent.children ?? [] : model.elements).filter((element) => decorations?.get(element.id)?.status !== 'deleted');
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
// Filter mode's view: the rows the search keeps stay, and the folders down to its matches open while it lasts;
|
|
2651
|
+
// null ends it. Each folder opens once, so one the reader closes meanwhile stays closed. Ending closes only the
|
|
2652
|
+
// folders it opened, bar those down to the cursor and the primary, which would otherwise drop out of view.
|
|
2653
|
+
//
|
|
2654
|
+
// Chains follow the view as they follow anything else hiding rows, so its rows are laid out first and the
|
|
2655
|
+
// folders opened on the chains it makes. A closed folder it folds into a compact row reads as open there, so it
|
|
2656
|
+
// counts as opened by the search, and closes again once the chain splits.
|
|
2657
|
+
function sift(folders: string[] | null) {
|
|
2658
|
+
let changed = false,
|
|
2659
|
+
next = new Set(folders),
|
|
2660
|
+
shut: string[] = [],
|
|
2661
|
+
spared = new Set<string>();
|
|
2662
|
+
|
|
2663
|
+
if (compact) {
|
|
2664
|
+
for (let row of built.values()) {
|
|
2665
|
+
if (row.open && !row.host && !peek(row.open)) {
|
|
2666
|
+
shut.push(row.id);
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
redisplay();
|
|
2672
|
+
|
|
2673
|
+
for (let i = 0, n = shut.length; i < n; i++) {
|
|
2674
|
+
if (built.get(shut[i])?.host && !unfolded.has(shut[i])) {
|
|
2675
|
+
unfolded.set(shut[i], true);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
if (!folders) {
|
|
2680
|
+
for (let row of [cursor, built.get(state.selected)]) {
|
|
2681
|
+
for (let node of row ? ancestors(row) : []) {
|
|
2682
|
+
spared.add(node.id);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
for (let [id, mine] of unfolded) {
|
|
2688
|
+
if (next.has(id)) {
|
|
2689
|
+
continue;
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
let row = built.get(id);
|
|
2693
|
+
|
|
2694
|
+
unfolded.delete(id);
|
|
2695
|
+
|
|
2696
|
+
if (!mine || spared.has(id) || !row?.open || !peek(row.open)) {
|
|
2697
|
+
continue;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
// One still folded counts among the open folders only once it shows its own row, closed now.
|
|
2701
|
+
write(row.open, false);
|
|
2702
|
+
opened -= row.host ? 0 : 1;
|
|
2703
|
+
changed = true;
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
for (let id of folders ?? []) {
|
|
2707
|
+
if (unfolded.has(id)) {
|
|
2708
|
+
continue;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
// Built with the folders above it.
|
|
2712
|
+
locate(id);
|
|
2713
|
+
|
|
2714
|
+
let row = built.get(id),
|
|
2715
|
+
open = !!row?.open && !row.host && !row.locked && !peek(row.open);
|
|
2716
|
+
|
|
2717
|
+
unfolded.set(id, open);
|
|
2718
|
+
|
|
2719
|
+
if (open) {
|
|
2720
|
+
write(row!.open!, true);
|
|
2721
|
+
opened++;
|
|
2722
|
+
changed = true;
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
redisplay(changed);
|
|
2727
|
+
ui.any = opened > 0;
|
|
2728
|
+
persist();
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// Shift+click and Shift+arrows: the rows from the anchor to 'to' become the selection. Without an anchor still on
|
|
2732
|
+
// screen, the range starts where the cursor was.
|
|
2733
|
+
function span(from: Row, to: Row) {
|
|
2734
|
+
if (!anchor || rows.indexOf(anchor) === -1) {
|
|
2735
|
+
anchor = from.locked ? to : from;
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
let a = rows.indexOf(anchor),
|
|
2739
|
+
b = rows.indexOf(to),
|
|
2740
|
+
ids: string[] = [];
|
|
2741
|
+
|
|
2742
|
+
for (let i = Math.min(a, b), n = Math.max(a, b); i <= n; i++) {
|
|
2743
|
+
if (!rows[i].locked) {
|
|
2744
|
+
ids.push(rows[i].id);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
selection.replace(ids);
|
|
2749
|
+
primary(anchor.locked ? (ids[0] ?? '') : anchor.id);
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// Whether the row compacts, as built, other than it would now.
|
|
2753
|
+
function stale(row: Row) {
|
|
2754
|
+
let now = joined(index.get(row.id)?.element ?? row.element),
|
|
2755
|
+
was = row.host ? lineage(row.host)[lineage(row).length] : undefined;
|
|
2756
|
+
|
|
2757
|
+
return (now?.id ?? null) !== (was?.id ?? null);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
// Replaces rows 'at' to 'end' with 'next', splicing only the stretch that differs so the rest stay rendered.
|
|
2761
|
+
function swap(at: number, end: number, next: Row[]) {
|
|
2762
|
+
let head = 0,
|
|
2763
|
+
n = next.length,
|
|
2764
|
+
tail = 0;
|
|
2765
|
+
|
|
2766
|
+
while (head < n && at + head < end && rows[at + head] === next[head]) {
|
|
2767
|
+
head++;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2770
|
+
while (tail < n - head && tail < end - at - head && rows[end - tail - 1] === next[n - tail - 1]) {
|
|
2771
|
+
tail++;
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
let items = next.slice(head, n - tail),
|
|
2775
|
+
removed = end - at - head - tail;
|
|
2776
|
+
|
|
2777
|
+
if (!items.length && !removed) {
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
rows.splice(at + head, removed, ...items.slice(0, CHUNK));
|
|
2782
|
+
insert(at + head + CHUNK, items.slice(CHUNK));
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
// Rebuilds a folder's rows from its element's children, or the top level's for null.
|
|
2786
|
+
function sync(target: string | null) {
|
|
2787
|
+
let row = target === null ? null : built.get(target);
|
|
2788
|
+
|
|
2789
|
+
// Never built, so it builds fresh when it opens.
|
|
2790
|
+
if (row === undefined || (row && !row.children)) {
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
let next: Row[];
|
|
2795
|
+
|
|
2796
|
+
if (row) {
|
|
2797
|
+
row.children = null;
|
|
2798
|
+
next = branch(row);
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
next = top = build(model.elements, 0, null);
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
let at = row ? rows.indexOf(row) + 1 : 0,
|
|
2805
|
+
end = row ? at : rows.length;
|
|
2806
|
+
|
|
2807
|
+
if (row && (at === 0 || !peek(row.open!))) {
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
while (row && end < rows.length && rows[end].depth > row.depth) {
|
|
2812
|
+
end++;
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
swap(at, end, visible(next));
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
// Adds or takes away one row's tone and lines along a chain of folders.
|
|
2819
|
+
function tally(start: string | null, value: number, lines: number[] | undefined, sign: 1 | -1) {
|
|
2820
|
+
if (value <= 0 && !lines) {
|
|
2821
|
+
return;
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
for (let parent = start; parent !== null; parent = index.get(parent)?.parent ?? null) {
|
|
2825
|
+
if (value > 0) {
|
|
2826
|
+
let list = counts.get(parent);
|
|
2827
|
+
|
|
2828
|
+
if (!list) {
|
|
2829
|
+
counts.set(parent, list = new Array(RANK.length).fill(0));
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
list[value] += sign;
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
if (lines) {
|
|
2836
|
+
let total = totals.get(parent);
|
|
2837
|
+
|
|
2838
|
+
if (!total) {
|
|
2839
|
+
totals.set(parent, total = [0, 0]);
|
|
2840
|
+
}
|
|
2841
|
+
|
|
2842
|
+
total[0] += sign * lines[0];
|
|
2843
|
+
total[1] += sign * lines[1];
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
refresh(parent);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
// What a command acts on: the selection when the focused row is part of it, else the focused row alone, as in
|
|
2851
|
+
// VS Code. Moving a selected folder carries its contents, so 'distinct' drops selected rows inside one. Roots
|
|
2852
|
+
// never move, so 'distinct' drops them instead, leaving what's selected inside them.
|
|
2853
|
+
function targets(row: Row, distinct: boolean) {
|
|
2854
|
+
let subject = segment(row);
|
|
2855
|
+
|
|
2856
|
+
if (!selection.has(subject.id)) {
|
|
2857
|
+
return subject.locked || (distinct && header(subject)) ? [] : [subject.element];
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
let out: Element[] = [];
|
|
2861
|
+
|
|
2862
|
+
for (let i = 0, n = rows.length; i < n; i++) {
|
|
2863
|
+
// Any of a compact row's folders can be selected.
|
|
2864
|
+
for (let candidate of lineage(rows[i])) {
|
|
2865
|
+
if (!selection.has(candidate.id)) {
|
|
2866
|
+
continue;
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
if (distinct && (header(candidate) || ancestors(candidate).some((node) => selection.has(node.id) && !header(node)))) {
|
|
2870
|
+
continue;
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
out.push(candidate.element);
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
return out;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
// Created when a row first renders; every later change is a single write the row alone reacts to.
|
|
2881
|
+
function track(id: string) {
|
|
2882
|
+
let mark = marks.get(id);
|
|
2883
|
+
|
|
2884
|
+
if (!mark) {
|
|
2885
|
+
marks.set(id, mark = signal(look(id)));
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
return mark;
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
function typeahead(character: string, from: number) {
|
|
2892
|
+
let now = performance.now(),
|
|
2893
|
+
next = (now - typedAt > TYPEAHEAD ? '' : typed) + character.toLowerCase(),
|
|
2894
|
+
// Repeating one letter cycles through the rows that start with it.
|
|
2895
|
+
repeat = [...next].every((c) => c === next[0]),
|
|
2896
|
+
start = repeat ? from + 1 : from;
|
|
2897
|
+
|
|
2898
|
+
typed = repeat ? next[0] : next;
|
|
2899
|
+
typedAt = now;
|
|
2900
|
+
|
|
2901
|
+
for (let i = 0, n = rows.length; i < n; i++) {
|
|
2902
|
+
let index = (start + i) % n;
|
|
2903
|
+
|
|
2904
|
+
if (caption(rows[index]).toLowerCase().startsWith(typed)) {
|
|
2905
|
+
return index;
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
return -1;
|
|
2910
|
+
}
|
|
2911
|
+
|
|
2912
|
+
// Decorated files the tree doesn't know yet, under ids that are paths: the deepest folder known along each one's
|
|
2913
|
+
// path loads, then the next one down inside what arrived, each at most once.
|
|
2914
|
+
async function uncover(kind: Jump) {
|
|
2915
|
+
let asked = new Set<string>();
|
|
2916
|
+
|
|
2917
|
+
for (;;) {
|
|
2918
|
+
let folders: Element[] = [];
|
|
2919
|
+
|
|
2920
|
+
for (let id of decorations!.keys()) {
|
|
2921
|
+
if (index.has(id) || !flagged(decorations!.get(id), kind)) {
|
|
2922
|
+
continue;
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
let parent = enclosing(id).find((candidate) => index.has(candidate)),
|
|
2926
|
+
element = parent === undefined ? undefined : index.get(parent)!.element;
|
|
2927
|
+
|
|
2928
|
+
if (!element || element.children || asked.has(element.id) || hidden.has(element.id) || !folder(element)) {
|
|
2929
|
+
continue;
|
|
2930
|
+
}
|
|
2931
|
+
|
|
2932
|
+
asked.add(element.id);
|
|
2933
|
+
folders.push(element);
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
if (!folders.length) {
|
|
2937
|
+
return;
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
// One at a time, so a jump across a large unloaded tree doesn't fire off every request at once.
|
|
2941
|
+
for (let i = 0, n = folders.length; i < n; i++) {
|
|
2942
|
+
await loader!.wait(folders[i]);
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
// A pinned folder's own row is scrolled back to where its copy sits, then clicked, so a folder that closes stays
|
|
2948
|
+
// under the pointer with the rows after it following on.
|
|
2949
|
+
function unpin(row: Row) {
|
|
2950
|
+
move(rows.indexOf(row), 'start');
|
|
2951
|
+
// Once the list has rendered the rows it scrolled to, so the folder closes with its motion.
|
|
2952
|
+
requestAnimationFrame(() => activate(row, PREVIEW));
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// Out of view: hidden for good, or left out by a search filtering the rows.
|
|
2956
|
+
function unseen(id: string) {
|
|
2957
|
+
return gone(id) || !!search?.hides(id);
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
// Rebuilds the folders a change touched, then finds the cursor again.
|
|
2961
|
+
function update(folders: (string | null)[]) {
|
|
2962
|
+
settle();
|
|
2963
|
+
|
|
2964
|
+
let at = cursor ? rows.indexOf(cursor) : 0;
|
|
2965
|
+
|
|
2966
|
+
for (let i = 0, n = folders.length; i < n; i++) {
|
|
2967
|
+
sync(regroup(folders[i]));
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
prune();
|
|
2971
|
+
clipboard.retain((id) => model.get(id));
|
|
2972
|
+
recover(at);
|
|
2973
|
+
ui.any = opened > 0;
|
|
2974
|
+
persist();
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
// The rows a reader can see: every row not inside a closed folder.
|
|
2978
|
+
function visible(list: Row[], out: Row[] = []) {
|
|
2979
|
+
let edit = peek(draft);
|
|
2980
|
+
|
|
2981
|
+
// A new item's input sits first in its folder.
|
|
2982
|
+
if (edit && !edit.element && list === (edit.parent ? edit.parent.children : top)) {
|
|
2983
|
+
out.push(edit.row);
|
|
2984
|
+
}
|
|
2985
|
+
|
|
2986
|
+
// Only a root with no children at all has a note, so one row hiding or showing never adds or drops it.
|
|
2987
|
+
if (roots && !list.length && list !== top) {
|
|
2988
|
+
let root = top.find((row) => row.children === list);
|
|
2989
|
+
|
|
2990
|
+
if (root) {
|
|
2991
|
+
out.push(blank(root));
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
for (let i = 0, n = list.length; i < n; i++) {
|
|
2996
|
+
let row = list[i];
|
|
2997
|
+
|
|
2998
|
+
if (hidden.has(row.id)) {
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
out.push(row);
|
|
3003
|
+
|
|
3004
|
+
if (row.open && peek(row.open)) {
|
|
3005
|
+
visible(branch(row), out);
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
return out;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
return html`
|
|
3013
|
+
<div
|
|
3014
|
+
class='file-tree ${indicator !== 'never' && 'file-tree--indicator'} ${indicator === 'hover' && 'file-tree--indicator-hover'}'
|
|
3015
|
+
${attributes}
|
|
3016
|
+
${dragging?.root}
|
|
3017
|
+
${{
|
|
3018
|
+
class: () => ui.motion && `file-tree--motion file-tree--motion-${ui.motion}`,
|
|
3019
|
+
onanimationend: (event: AnimationEvent) => {
|
|
3020
|
+
if (event.target === root) {
|
|
3021
|
+
settle();
|
|
3022
|
+
}
|
|
3023
|
+
},
|
|
3024
|
+
onconnect: (element: HTMLElement) => {
|
|
3025
|
+
root = element;
|
|
3026
|
+
forget = history?.subscribe(retrace);
|
|
3027
|
+
unwatch = model.subscribe(restructure);
|
|
3028
|
+
|
|
3029
|
+
if (seen !== model.version) {
|
|
3030
|
+
resync();
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
// These effects are made on connect rather than while building, where they would belong to whatever
|
|
3034
|
+
// computation built the tree and rerun it; the connection owns them and they end with it.
|
|
3035
|
+
|
|
3036
|
+
// Selection written from outside, like an editor switching tabs, becomes the whole selection and
|
|
3037
|
+
// opens the folders down to it.
|
|
3038
|
+
effect(() => state.selected, (value) => {
|
|
3039
|
+
write(current, value);
|
|
3040
|
+
|
|
3041
|
+
if (value === known) {
|
|
3042
|
+
return;
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
known = value;
|
|
3046
|
+
|
|
3047
|
+
if (!selection.has(value)) {
|
|
3048
|
+
anchor = built.get(value) ?? null;
|
|
3049
|
+
selection.replace(value ? [value] : []);
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
if (value !== quiet && autoreveal !== 'off') {
|
|
3053
|
+
reveal(value, autoreveal === 'on');
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
quiet = null;
|
|
3057
|
+
});
|
|
3058
|
+
|
|
3059
|
+
effect(() => state.selection, (value) => {
|
|
3060
|
+
if (!value || value === selection.ids) {
|
|
3061
|
+
return;
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
selection.replace(value);
|
|
3065
|
+
|
|
3066
|
+
if (!value.has(state.selected)) {
|
|
3067
|
+
primary(value.values().next().value ?? '');
|
|
3068
|
+
}
|
|
3069
|
+
});
|
|
3070
|
+
|
|
3071
|
+
// Display written from outside, like a settings toggle, hides and shows rows in place.
|
|
3072
|
+
effect(modes, (value) => {
|
|
3073
|
+
if (value !== settings) {
|
|
3074
|
+
settings = value;
|
|
3075
|
+
filtered = filter(model.elements, display.dotfiles !== false, excluded, roots);
|
|
3076
|
+
redisplay();
|
|
3077
|
+
search?.refresh();
|
|
3078
|
+
}
|
|
3079
|
+
});
|
|
3080
|
+
|
|
3081
|
+
if (typeof phrase === 'function') {
|
|
3082
|
+
effect(phrase, (value) => {
|
|
3083
|
+
write(given, value);
|
|
3084
|
+
});
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
// Changes made while disconnected went unsearched.
|
|
3088
|
+
search?.refresh();
|
|
3089
|
+
|
|
3090
|
+
if (snapshot?.scroll) {
|
|
3091
|
+
let offset = snapshot.scroll;
|
|
3092
|
+
|
|
3093
|
+
requestAnimationFrame(() => {
|
|
3094
|
+
viewport!.scrollTop = offset;
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3098
|
+
detach = editor?.subscribe((request) => {
|
|
3099
|
+
if (request.action === 'cancel') {
|
|
3100
|
+
close(!!viewport?.contains(document.activeElement));
|
|
3101
|
+
}
|
|
3102
|
+
else {
|
|
3103
|
+
begin(request.action, request.target);
|
|
3104
|
+
}
|
|
3105
|
+
});
|
|
3106
|
+
|
|
3107
|
+
if (!decorations) {
|
|
3108
|
+
return;
|
|
3109
|
+
}
|
|
3110
|
+
|
|
3111
|
+
// A file shown again, no longer ignored or deleted, may match.
|
|
3112
|
+
unsubscribe = decorations.subscribe((ids) => {
|
|
3113
|
+
decorate(ids);
|
|
3114
|
+
search?.refresh();
|
|
3115
|
+
});
|
|
3116
|
+
// Catches up on whatever changed while disconnected, including ids since cleared.
|
|
3117
|
+
decorate(new Set([...own.keys(), ...decorations.keys()]));
|
|
3118
|
+
},
|
|
3119
|
+
ondisconnect: () => {
|
|
3120
|
+
clearTimeout(timer);
|
|
3121
|
+
search?.dispose();
|
|
3122
|
+
detach?.();
|
|
3123
|
+
detach = undefined;
|
|
3124
|
+
|
|
3125
|
+
forget?.();
|
|
3126
|
+
forget = undefined;
|
|
3127
|
+
unsubscribe?.();
|
|
3128
|
+
unsubscribe = undefined;
|
|
3129
|
+
unwatch?.();
|
|
3130
|
+
unwatch = undefined;
|
|
3131
|
+
},
|
|
3132
|
+
style: () => ui.motion && `--motion-rows: ${ui.motionRows}`
|
|
3133
|
+
}}
|
|
3134
|
+
>
|
|
3135
|
+
<div
|
|
3136
|
+
aria-label='${label}'
|
|
3137
|
+
aria-multiselectable='true'
|
|
3138
|
+
class='file-tree-viewport'
|
|
3139
|
+
id='${id}'
|
|
3140
|
+
role='tree'
|
|
3141
|
+
tabindex='0'
|
|
3142
|
+
${dragging?.viewport}
|
|
3143
|
+
${{
|
|
3144
|
+
'aria-activedescendant': () => read(focused) > 0 && `${id}-${read(focused)}`,
|
|
3145
|
+
onconnect: (element: HTMLElement) => {
|
|
3146
|
+
viewport = element;
|
|
3147
|
+
},
|
|
3148
|
+
oncontextmenu: (event: MouseEvent) => {
|
|
3149
|
+
if (!menu || !viewport) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
event.preventDefault();
|
|
3154
|
+
|
|
3155
|
+
let item = (event.target as HTMLElement).closest<Rendered>('.file-tree-row'),
|
|
3156
|
+
// Shift+F10 and the menu key fire on the tree itself, with no pointer to place the menu at.
|
|
3157
|
+
keyboard = !item && event.button !== 2,
|
|
3158
|
+
row = keyboard ? cursor : owner(item);
|
|
3159
|
+
|
|
3160
|
+
if (!row) {
|
|
3161
|
+
menu([], { x: event.clientX, y: event.clientY }, false);
|
|
3162
|
+
return;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
let subject = keyboard ? segment(row) : pick(row, event);
|
|
3166
|
+
|
|
3167
|
+
if (!subject.locked && !selection.has(subject.id)) {
|
|
3168
|
+
anchor = row;
|
|
3169
|
+
selection.replace([subject.id]);
|
|
3170
|
+
primary(subject.id);
|
|
3171
|
+
}
|
|
3172
|
+
|
|
3173
|
+
focus(row);
|
|
3174
|
+
|
|
3175
|
+
let box = keyboard ? (node(row) ?? viewport).getBoundingClientRect() : null;
|
|
3176
|
+
|
|
3177
|
+
menu(targets(row, false), box ? { x: box.left, y: box.bottom } : { x: event.clientX, y: event.clientY }, header(row));
|
|
3178
|
+
},
|
|
3179
|
+
onkeydown: (event: KeyboardEvent) => {
|
|
3180
|
+
if (search?.key(event)) {
|
|
3181
|
+
event.preventDefault();
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
let index = cursor ? rows.indexOf(cursor) : -1,
|
|
3186
|
+
row = rows[index];
|
|
3187
|
+
|
|
3188
|
+
if (!row || !viewport) {
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
let name = command(event);
|
|
3193
|
+
|
|
3194
|
+
if (name && perform(name, row)) {
|
|
3195
|
+
event.preventDefault();
|
|
3196
|
+
return;
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
let align: Align = 'end',
|
|
3200
|
+
target = -1;
|
|
3201
|
+
|
|
3202
|
+
switch (event.key) {
|
|
3203
|
+
// Opens every folder beside the cursor's row, one level deep.
|
|
3204
|
+
case '*': {
|
|
3205
|
+
let siblings = row.parent ? branch(row.parent) : top;
|
|
3206
|
+
|
|
3207
|
+
for (let i = 0, n = siblings.length; i < n; i++) {
|
|
3208
|
+
if (!siblings[i].locked) {
|
|
3209
|
+
expand(siblings[i], true);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
break;
|
|
3214
|
+
}
|
|
3215
|
+
case 'ArrowDown':
|
|
3216
|
+
target = index + 1;
|
|
3217
|
+
break;
|
|
3218
|
+
case 'ArrowUp':
|
|
3219
|
+
align = 'start';
|
|
3220
|
+
target = index - 1;
|
|
3221
|
+
break;
|
|
3222
|
+
case 'End':
|
|
3223
|
+
target = rows.length - 1;
|
|
3224
|
+
break;
|
|
3225
|
+
case 'Enter':
|
|
3226
|
+
case ' ':
|
|
3227
|
+
activate(row, event.key === ' ' ? PREVIEW : { mode: 'pinned', side: event.ctrlKey || event.metaKey });
|
|
3228
|
+
break;
|
|
3229
|
+
// Next and previous change, as in VS Code's editor.
|
|
3230
|
+
case 'F5':
|
|
3231
|
+
if (!event.altKey || event.ctrlKey || event.metaKey) {
|
|
3232
|
+
return;
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
void jump('change', event.shiftKey ? -1 : 1);
|
|
3236
|
+
break;
|
|
3237
|
+
// Next and previous problem.
|
|
3238
|
+
case 'F8':
|
|
3239
|
+
if (event.altKey || event.ctrlKey || event.metaKey) {
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
|
|
3243
|
+
void jump('problem', event.shiftKey ? -1 : 1);
|
|
3244
|
+
break;
|
|
3245
|
+
case 'F2':
|
|
3246
|
+
begin('rename');
|
|
3247
|
+
break;
|
|
3248
|
+
case 'Home':
|
|
3249
|
+
align = 'start';
|
|
3250
|
+
target = 0;
|
|
3251
|
+
break;
|
|
3252
|
+
case 'PageDown':
|
|
3253
|
+
target = Math.min(rows.length - 1, index + page());
|
|
3254
|
+
break;
|
|
3255
|
+
case 'PageUp':
|
|
3256
|
+
align = 'start';
|
|
3257
|
+
target = Math.max(0, index - page());
|
|
3258
|
+
break;
|
|
3259
|
+
case 'ArrowLeft':
|
|
3260
|
+
case 'ArrowRight': {
|
|
3261
|
+
// Arrows point into and out of folders; flipped for right-to-left layouts.
|
|
3262
|
+
let inward = (event.key === 'ArrowRight') !== (getComputedStyle(viewport).direction === 'rtl'),
|
|
3263
|
+
open = row.open && peek(row.open);
|
|
3264
|
+
|
|
3265
|
+
// Alt opens or closes the folder with everything in it.
|
|
3266
|
+
if (event.altKey && row.open && (inward || open)) {
|
|
3267
|
+
all(inward, row);
|
|
3268
|
+
}
|
|
3269
|
+
else if (inward && row.open && !row.locked) {
|
|
3270
|
+
if (!open) {
|
|
3271
|
+
fold(row, true);
|
|
3272
|
+
}
|
|
3273
|
+
else if (row.element.children?.length) {
|
|
3274
|
+
target = index + 1;
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
else if (!inward && open) {
|
|
3278
|
+
fold(row, false);
|
|
3279
|
+
}
|
|
3280
|
+
else if (!inward && row.parent) {
|
|
3281
|
+
align = 'start';
|
|
3282
|
+
target = rows.indexOf(row.parent);
|
|
3283
|
+
}
|
|
3284
|
+
break;
|
|
3285
|
+
}
|
|
3286
|
+
default:
|
|
3287
|
+
// By code, since macOS turns Alt+N into a symbol.
|
|
3288
|
+
if (shortcuts && event.altKey && event.code === 'KeyN' && !event.ctrlKey && !event.metaKey) {
|
|
3289
|
+
begin(event.shiftKey ? 'folder' : 'file');
|
|
3290
|
+
break;
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
if (event.key.length !== 1 || event.altKey || event.ctrlKey || event.metaKey) {
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
if (search && typing === 'find') {
|
|
3298
|
+
search.type(event.key);
|
|
3299
|
+
}
|
|
3300
|
+
else {
|
|
3301
|
+
target = typeahead(event.key, index);
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
event.preventDefault();
|
|
3306
|
+
|
|
3307
|
+
if (target !== -1) {
|
|
3308
|
+
if (rows[target] && extending(event)) {
|
|
3309
|
+
span(row, rows[target]);
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
move(target, align);
|
|
3313
|
+
|
|
3314
|
+
if (cursor && cursor !== row) {
|
|
3315
|
+
follow(cursor);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
},
|
|
3319
|
+
onmousedown: middle,
|
|
3320
|
+
onscroll: () => {
|
|
3321
|
+
// The copies are laid out against the rows as they stood; once scrolled they no longer line up.
|
|
3322
|
+
settle();
|
|
3323
|
+
pins.update();
|
|
3324
|
+
|
|
3325
|
+
if (snapshot) {
|
|
3326
|
+
snapshot.scroll = viewport!.scrollTop;
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
}}
|
|
3330
|
+
${hint && {
|
|
3331
|
+
onpointerleave: (event: PointerEvent) => {
|
|
3332
|
+
if (event.pointerType !== 'touch') {
|
|
3333
|
+
tip.release();
|
|
3334
|
+
}
|
|
3335
|
+
},
|
|
3336
|
+
onpointerover: (event: PointerEvent) => {
|
|
3337
|
+
let scroller = event.currentTarget as HTMLElement,
|
|
3338
|
+
target = (event.target as HTMLElement).closest<Rendered>('.file-tree-row'),
|
|
3339
|
+
row = owner(target);
|
|
3340
|
+
|
|
3341
|
+
// Between rows, in the space beside a nested one, the tooltip stays on the last row.
|
|
3342
|
+
if (!target || !row) {
|
|
3343
|
+
return;
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3346
|
+
if (!informative(row, target)) {
|
|
3347
|
+
tip.release();
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
// Past the tree's edge, in line with the row, so it glides straight up and down between rows.
|
|
3352
|
+
tip.request(target, about(row), () => {
|
|
3353
|
+
let box = scroller.getBoundingClientRect(),
|
|
3354
|
+
rect = target.getBoundingClientRect();
|
|
3355
|
+
|
|
3356
|
+
return new DOMRect(box.left, rect.top, box.width, rect.height);
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
}}
|
|
3360
|
+
>
|
|
3361
|
+
${highlight({ class: 'file-tree-highlight', target: '.file-tree-row' })}
|
|
3362
|
+
${pins.render()}
|
|
3363
|
+
${slot.fragment}
|
|
3364
|
+
</div>
|
|
3365
|
+
|
|
3366
|
+
<div
|
|
3367
|
+
aria-hidden='true'
|
|
3368
|
+
class='file-tree-motion'
|
|
3369
|
+
inert
|
|
3370
|
+
style='${() => `height: ${stage.height}px; left: ${stage.left}px; top: ${stage.top}px; width: ${stage.width}px;`}'
|
|
3371
|
+
${{ class: () => ui.motion && '--active' }}
|
|
3372
|
+
>
|
|
3373
|
+
<div
|
|
3374
|
+
class='file-tree-motion-layer'
|
|
3375
|
+
style='${() => `top: ${stage.edge}px;`}'
|
|
3376
|
+
${{ class: () => ui.motion && `file-tree-motion-layer--${ui.motion}` }}
|
|
3377
|
+
>
|
|
3378
|
+
${html.reactive(ghosts, ghost)}
|
|
3379
|
+
</div>
|
|
3380
|
+
</div>
|
|
3381
|
+
|
|
3382
|
+
${() => empty && !rows.length && !search?.filtering() && html`<div class='file-tree-empty'>${empty()}</div>`}
|
|
3383
|
+
|
|
3384
|
+
${search?.render(id)}
|
|
3385
|
+
|
|
3386
|
+
${hint && tip.render({ 'aria-hidden': 'true', class: 'file-tree-tooltip' })}
|
|
3387
|
+
|
|
3388
|
+
${toggle && html`
|
|
3389
|
+
<button
|
|
3390
|
+
class='button file-tree-toggle'
|
|
3391
|
+
onclick='${() => all(!ui.any)}'
|
|
3392
|
+
type='button'
|
|
3393
|
+
>
|
|
3394
|
+
${() => ui.any ? 'Collapse all' : 'Expand all'}
|
|
3395
|
+
</button>
|
|
3396
|
+
`}
|
|
3397
|
+
|
|
3398
|
+
${dragging?.ghost}
|
|
3399
|
+
</div>
|
|
3400
|
+
`;
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3403
|
+
export { Decorations as FileTreeDecorations, Editor as FileTreeEditor, Elements as FileTreeElements, History as FileTreeHistory };
|
|
3404
|
+
export { createFileTreeIconResolver, glyph as fileTreeIcon, resolveFileTreeIcon };
|
|
3405
|
+
export type {
|
|
3406
|
+
Badge as FileTreeBadge,
|
|
3407
|
+
Change as FileTreeChange,
|
|
3408
|
+
Controller as FileTreeController,
|
|
3409
|
+
Decoration as FileTreeDecoration,
|
|
3410
|
+
Drag as FileTreeDrag,
|
|
3411
|
+
Drop as FileTreeDrop,
|
|
3412
|
+
Element as FileTreeElement,
|
|
3413
|
+
Export as FileTreeExport,
|
|
3414
|
+
HistoryEntry as FileTreeHistoryEntry,
|
|
3415
|
+
Operation as FileTreeHistoryOperation,
|
|
3416
|
+
HistoryOptions as FileTreeHistoryOptions,
|
|
3417
|
+
Place as FileTreeHistoryPlace,
|
|
3418
|
+
Step as FileTreeHistoryStep,
|
|
3419
|
+
IconName as FileTreeIconName,
|
|
3420
|
+
IconOptions as FileTreeIconOptions,
|
|
3421
|
+
ImportEntry as FileTreeImportEntry,
|
|
3422
|
+
Indicator as FileTreeIndicator,
|
|
3423
|
+
Jump as FileTreeJump,
|
|
3424
|
+
Kind as FileTreeKind,
|
|
3425
|
+
Load as FileTreeLoad,
|
|
3426
|
+
Open as FileTreeOpen,
|
|
3427
|
+
Operations as FileTreeOperations,
|
|
3428
|
+
ResolvedIcon as FileTreeResolvedIcon,
|
|
3429
|
+
Result as FileTreeResult,
|
|
3430
|
+
Scope as FileTreeScope,
|
|
3431
|
+
Snapshot as FileTreeSnapshot,
|
|
3432
|
+
Sort as FileTreeSort,
|
|
3433
|
+
Case as FileTreeSortCase,
|
|
3434
|
+
Options as FileTreeSortOptions,
|
|
3435
|
+
Order as FileTreeSortOrder,
|
|
3436
|
+
State as FileTreeState,
|
|
3437
|
+
Status as FileTreeStatus
|
|
3438
|
+
};
|