@cardstack/boxel-cli 0.10.0 → 0.10.1-unstable.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/bundled-realms/pretui/README.md +49 -0
- package/bundled-realms/pretui/color/LICENSE +21 -0
- package/bundled-realms/pretui/color/README.md +155 -0
- package/bundled-realms/pretui/color/index.js +78 -0
- package/bundled-realms/pretui/color-engine.ts +1243 -0
- package/bundled-realms/pretui/components/accordion.gts +58 -0
- package/bundled-realms/pretui/components/accordion.md +60 -0
- package/bundled-realms/pretui/components/accordion.usage.gts +129 -0
- package/bundled-realms/pretui/components/action-bar.gts +407 -0
- package/bundled-realms/pretui/components/action-bar.md +48 -0
- package/bundled-realms/pretui/components/action-bar.usage.gts +213 -0
- package/bundled-realms/pretui/components/affix.gts +116 -0
- package/bundled-realms/pretui/components/affix.md +54 -0
- package/bundled-realms/pretui/components/affix.usage.gts +73 -0
- package/bundled-realms/pretui/components/agent-question.gts +116 -0
- package/bundled-realms/pretui/components/agent-question.md +50 -0
- package/bundled-realms/pretui/components/agent-question.usage.gts +22 -0
- package/bundled-realms/pretui/components/ai-instructions.gts +378 -0
- package/bundled-realms/pretui/components/ai-instructions.md +49 -0
- package/bundled-realms/pretui/components/ai-instructions.usage.gts +101 -0
- package/bundled-realms/pretui/components/alert-dialog.gts +223 -0
- package/bundled-realms/pretui/components/alert-dialog.md +76 -0
- package/bundled-realms/pretui/components/alert-dialog.usage.gts +128 -0
- package/bundled-realms/pretui/components/alert.gts +163 -0
- package/bundled-realms/pretui/components/alert.md +57 -0
- package/bundled-realms/pretui/components/alert.usage.gts +99 -0
- package/bundled-realms/pretui/components/amount-input.gts +621 -0
- package/bundled-realms/pretui/components/amount-input.md +63 -0
- package/bundled-realms/pretui/components/amount-input.usage.gts +170 -0
- package/bundled-realms/pretui/components/angle-dial.gts +367 -0
- package/bundled-realms/pretui/components/angle-dial.md +56 -0
- package/bundled-realms/pretui/components/angle-dial.usage.gts +166 -0
- package/bundled-realms/pretui/components/animated-image.gts +291 -0
- package/bundled-realms/pretui/components/animated-image.md +49 -0
- package/bundled-realms/pretui/components/animated-image.usage.gts +127 -0
- package/bundled-realms/pretui/components/app-shell.gts +19 -0
- package/bundled-realms/pretui/components/app-shell.md +43 -0
- package/bundled-realms/pretui/components/app-shell.usage.gts +52 -0
- package/bundled-realms/pretui/components/approval-footer.gts +69 -0
- package/bundled-realms/pretui/components/approval-footer.md +49 -0
- package/bundled-realms/pretui/components/approval-footer.usage.gts +22 -0
- package/bundled-realms/pretui/components/aspect-ratio.gts +328 -0
- package/bundled-realms/pretui/components/aspect-ratio.md +72 -0
- package/bundled-realms/pretui/components/aspect-ratio.usage.gts +174 -0
- package/bundled-realms/pretui/components/asset-grid.gts +542 -0
- package/bundled-realms/pretui/components/asset-grid.md +54 -0
- package/bundled-realms/pretui/components/asset-grid.usage.gts +171 -0
- package/bundled-realms/pretui/components/asset-well.gts +731 -0
- package/bundled-realms/pretui/components/asset-well.md +72 -0
- package/bundled-realms/pretui/components/asset-well.usage.gts +264 -0
- package/bundled-realms/pretui/components/audio-player.gts +152 -0
- package/bundled-realms/pretui/components/audio-player.md +51 -0
- package/bundled-realms/pretui/components/audio-player.usage.gts +118 -0
- package/bundled-realms/pretui/components/autocomplete.gts +1150 -0
- package/bundled-realms/pretui/components/autocomplete.md +67 -0
- package/bundled-realms/pretui/components/autocomplete.usage.gts +339 -0
- package/bundled-realms/pretui/components/avatar-group.gts +30 -0
- package/bundled-realms/pretui/components/avatar-group.md +48 -0
- package/bundled-realms/pretui/components/avatar-group.usage.gts +39 -0
- package/bundled-realms/pretui/components/avatar.gts +134 -0
- package/bundled-realms/pretui/components/avatar.md +69 -0
- package/bundled-realms/pretui/components/avatar.usage.gts +101 -0
- package/bundled-realms/pretui/components/backdrop.gts +179 -0
- package/bundled-realms/pretui/components/backdrop.md +56 -0
- package/bundled-realms/pretui/components/backdrop.usage.gts +223 -0
- package/bundled-realms/pretui/components/background-field.gts +743 -0
- package/bundled-realms/pretui/components/background-field.md +62 -0
- package/bundled-realms/pretui/components/background-field.usage.gts +328 -0
- package/bundled-realms/pretui/components/badge.gts +188 -0
- package/bundled-realms/pretui/components/badge.md +62 -0
- package/bundled-realms/pretui/components/badge.usage.gts +73 -0
- package/bundled-realms/pretui/components/bar-list.gts +255 -0
- package/bundled-realms/pretui/components/bar-list.md +49 -0
- package/bundled-realms/pretui/components/bar-list.usage.gts +129 -0
- package/bundled-realms/pretui/components/board.gts +108 -0
- package/bundled-realms/pretui/components/board.md +54 -0
- package/bundled-realms/pretui/components/board.usage.gts +321 -0
- package/bundled-realms/pretui/components/bottom-nav.gts +217 -0
- package/bundled-realms/pretui/components/bottom-nav.md +64 -0
- package/bundled-realms/pretui/components/bottom-nav.usage.gts +68 -0
- package/bundled-realms/pretui/components/breadcrumb.gts +56 -0
- package/bundled-realms/pretui/components/breadcrumb.md +52 -0
- package/bundled-realms/pretui/components/breadcrumb.usage.gts +24 -0
- package/bundled-realms/pretui/components/broken-link.gts +46 -0
- package/bundled-realms/pretui/components/broken-link.md +50 -0
- package/bundled-realms/pretui/components/broken-link.usage.gts +57 -0
- package/bundled-realms/pretui/components/button-group.gts +246 -0
- package/bundled-realms/pretui/components/button-group.md +46 -0
- package/bundled-realms/pretui/components/button-group.usage.gts +137 -0
- package/bundled-realms/pretui/components/button.examples.gts +67 -0
- package/bundled-realms/pretui/components/button.gts +594 -0
- package/bundled-realms/pretui/components/button.md +81 -0
- package/bundled-realms/pretui/components/button.usage.gts +159 -0
- package/bundled-realms/pretui/components/calendar.gts +457 -0
- package/bundled-realms/pretui/components/calendar.md +57 -0
- package/bundled-realms/pretui/components/calendar.usage.gts +161 -0
- package/bundled-realms/pretui/components/callout.gts +2 -0
- package/bundled-realms/pretui/components/callout.md +40 -0
- package/bundled-realms/pretui/components/callout.usage.gts +48 -0
- package/bundled-realms/pretui/components/card.gts +637 -0
- package/bundled-realms/pretui/components/card.md +105 -0
- package/bundled-realms/pretui/components/card.usage.gts +279 -0
- package/bundled-realms/pretui/components/carousel.gts +583 -0
- package/bundled-realms/pretui/components/carousel.md +53 -0
- package/bundled-realms/pretui/components/carousel.usage.gts +220 -0
- package/bundled-realms/pretui/components/cascader.gts +436 -0
- package/bundled-realms/pretui/components/cascader.md +61 -0
- package/bundled-realms/pretui/components/cascader.usage.gts +79 -0
- package/bundled-realms/pretui/components/channel-slider.gts +251 -0
- package/bundled-realms/pretui/components/channel-slider.md +51 -0
- package/bundled-realms/pretui/components/channel-slider.usage.gts +104 -0
- package/bundled-realms/pretui/components/chart.gts +1011 -0
- package/bundled-realms/pretui/components/chart.md +60 -0
- package/bundled-realms/pretui/components/chart.usage.gts +660 -0
- package/bundled-realms/pretui/components/checkbox-card.gts +82 -0
- package/bundled-realms/pretui/components/checkbox-card.md +73 -0
- package/bundled-realms/pretui/components/checkbox-card.usage.gts +114 -0
- package/bundled-realms/pretui/components/checkbox.examples.gts +61 -0
- package/bundled-realms/pretui/components/checkbox.gts +123 -0
- package/bundled-realms/pretui/components/checkbox.md +55 -0
- package/bundled-realms/pretui/components/checkbox.usage.gts +31 -0
- package/bundled-realms/pretui/components/chip.gts +136 -0
- package/bundled-realms/pretui/components/chip.md +54 -0
- package/bundled-realms/pretui/components/chip.usage.gts +97 -0
- package/bundled-realms/pretui/components/code-block.gts +888 -0
- package/bundled-realms/pretui/components/code-block.md +61 -0
- package/bundled-realms/pretui/components/code-block.usage.gts +374 -0
- package/bundled-realms/pretui/components/collapsed-turn.gts +44 -0
- package/bundled-realms/pretui/components/collapsed-turn.md +48 -0
- package/bundled-realms/pretui/components/collapsed-turn.usage.gts +21 -0
- package/bundled-realms/pretui/components/collapsible.gts +283 -0
- package/bundled-realms/pretui/components/collapsible.md +69 -0
- package/bundled-realms/pretui/components/collapsible.usage.gts +173 -0
- package/bundled-realms/pretui/components/color-area.gts +339 -0
- package/bundled-realms/pretui/components/color-area.md +49 -0
- package/bundled-realms/pretui/components/color-area.usage.gts +72 -0
- package/bundled-realms/pretui/components/color-field.gts +179 -0
- package/bundled-realms/pretui/components/color-field.md +47 -0
- package/bundled-realms/pretui/components/color-field.usage.gts +87 -0
- package/bundled-realms/pretui/components/color-palette.gts +198 -0
- package/bundled-realms/pretui/components/color-palette.md +48 -0
- package/bundled-realms/pretui/components/color-picker.gts +721 -0
- package/bundled-realms/pretui/components/color-picker.md +59 -0
- package/bundled-realms/pretui/components/color-picker.usage.gts +122 -0
- package/bundled-realms/pretui/components/color-stop-editor.gts +184 -0
- package/bundled-realms/pretui/components/color-stop-editor.md +46 -0
- package/bundled-realms/pretui/components/color-stop-editor.usage.gts +75 -0
- package/bundled-realms/pretui/components/combobox.gts +333 -0
- package/bundled-realms/pretui/components/combobox.md +49 -0
- package/bundled-realms/pretui/components/combobox.usage.gts +143 -0
- package/bundled-realms/pretui/components/command-palette.gts +982 -0
- package/bundled-realms/pretui/components/command-palette.md +47 -0
- package/bundled-realms/pretui/components/command-palette.usage.gts +95 -0
- package/bundled-realms/pretui/components/command.gts +2 -0
- package/bundled-realms/pretui/components/command.md +45 -0
- package/bundled-realms/pretui/components/command.usage.gts +58 -0
- package/bundled-realms/pretui/components/compaction-chip.gts +273 -0
- package/bundled-realms/pretui/components/compaction-chip.md +45 -0
- package/bundled-realms/pretui/components/compaction-chip.usage.gts +110 -0
- package/bundled-realms/pretui/components/comparison.gts +223 -0
- package/bundled-realms/pretui/components/comparison.md +46 -0
- package/bundled-realms/pretui/components/comparison.usage.gts +148 -0
- package/bundled-realms/pretui/components/composer.gts +566 -0
- package/bundled-realms/pretui/components/composer.md +59 -0
- package/bundled-realms/pretui/components/composer.usage.gts +173 -0
- package/bundled-realms/pretui/components/compound-field.gts +289 -0
- package/bundled-realms/pretui/components/compound-field.md +66 -0
- package/bundled-realms/pretui/components/compound-field.usage.gts +378 -0
- package/bundled-realms/pretui/components/contact-link.gts +583 -0
- package/bundled-realms/pretui/components/contact-link.md +53 -0
- package/bundled-realms/pretui/components/contact-link.usage.gts +273 -0
- package/bundled-realms/pretui/components/context-menu.gts +811 -0
- package/bundled-realms/pretui/components/context-menu.md +74 -0
- package/bundled-realms/pretui/components/context-menu.usage.gts +244 -0
- package/bundled-realms/pretui/components/copy-button.gts +237 -0
- package/bundled-realms/pretui/components/copy-button.md +71 -0
- package/bundled-realms/pretui/components/copy-button.usage.gts +135 -0
- package/bundled-realms/pretui/components/copy-fit.gts +396 -0
- package/bundled-realms/pretui/components/copy-fit.md +60 -0
- package/bundled-realms/pretui/components/copy-fit.usage.gts +200 -0
- package/bundled-realms/pretui/components/cta-band.gts +285 -0
- package/bundled-realms/pretui/components/cta-band.md +50 -0
- package/bundled-realms/pretui/components/cta-band.usage.gts +208 -0
- package/bundled-realms/pretui/components/cue.gts +180 -0
- package/bundled-realms/pretui/components/cue.md +53 -0
- package/bundled-realms/pretui/components/cue.usage.gts +88 -0
- package/bundled-realms/pretui/components/cursor-trail.gts +221 -0
- package/bundled-realms/pretui/components/cursor-trail.md +56 -0
- package/bundled-realms/pretui/components/cursor-trail.usage.gts +218 -0
- package/bundled-realms/pretui/components/dashboard-grid.gts +939 -0
- package/bundled-realms/pretui/components/dashboard-grid.md +60 -0
- package/bundled-realms/pretui/components/dashboard-grid.usage.gts +307 -0
- package/bundled-realms/pretui/components/dashboard-item.gts +286 -0
- package/bundled-realms/pretui/components/dashboard-item.md +52 -0
- package/bundled-realms/pretui/components/dashboard-item.usage.gts +87 -0
- package/bundled-realms/pretui/components/data-grid.gts +263 -0
- package/bundled-realms/pretui/components/data-grid.md +64 -0
- package/bundled-realms/pretui/components/data-grid.usage.gts +50 -0
- package/bundled-realms/pretui/components/data-table.gts +1618 -0
- package/bundled-realms/pretui/components/data-table.md +104 -0
- package/bundled-realms/pretui/components/data-table.usage.gts +324 -0
- package/bundled-realms/pretui/components/date-picker.gts +98 -0
- package/bundled-realms/pretui/components/date-picker.md +59 -0
- package/bundled-realms/pretui/components/date-picker.usage.gts +66 -0
- package/bundled-realms/pretui/components/date-range-picker.gts +131 -0
- package/bundled-realms/pretui/components/date-range-picker.md +55 -0
- package/bundled-realms/pretui/components/date-range-picker.usage.gts +125 -0
- package/bundled-realms/pretui/components/date-time-picker.gts +227 -0
- package/bundled-realms/pretui/components/date-time-picker.md +60 -0
- package/bundled-realms/pretui/components/date-time-picker.usage.gts +64 -0
- package/bundled-realms/pretui/components/defer.gts +374 -0
- package/bundled-realms/pretui/components/defer.md +61 -0
- package/bundled-realms/pretui/components/defer.usage.gts +174 -0
- package/bundled-realms/pretui/components/delta.gts +45 -0
- package/bundled-realms/pretui/components/delta.md +53 -0
- package/bundled-realms/pretui/components/delta.usage.gts +31 -0
- package/bundled-realms/pretui/components/descriptions.gts +365 -0
- package/bundled-realms/pretui/components/descriptions.md +73 -0
- package/bundled-realms/pretui/components/descriptions.usage.gts +123 -0
- package/bundled-realms/pretui/components/dialog.gts +153 -0
- package/bundled-realms/pretui/components/dialog.md +60 -0
- package/bundled-realms/pretui/components/dialog.usage.gts +86 -0
- package/bundled-realms/pretui/components/diff-block.gts +89 -0
- package/bundled-realms/pretui/components/diff-block.md +51 -0
- package/bundled-realms/pretui/components/diff-block.usage.gts +27 -0
- package/bundled-realms/pretui/components/disclosure.gts +2 -0
- package/bundled-realms/pretui/components/disclosure.md +45 -0
- package/bundled-realms/pretui/components/disclosure.usage.gts +47 -0
- package/bundled-realms/pretui/components/divider.gts +75 -0
- package/bundled-realms/pretui/components/divider.md +49 -0
- package/bundled-realms/pretui/components/divider.usage.gts +98 -0
- package/bundled-realms/pretui/components/doc-report.gts +306 -0
- package/bundled-realms/pretui/components/doc-report.md +45 -0
- package/bundled-realms/pretui/components/doc-report.usage.gts +126 -0
- package/bundled-realms/pretui/components/dock.gts +165 -0
- package/bundled-realms/pretui/components/dock.md +42 -0
- package/bundled-realms/pretui/components/dock.usage.gts +114 -0
- package/bundled-realms/pretui/components/drawer.gts +166 -0
- package/bundled-realms/pretui/components/drawer.md +66 -0
- package/bundled-realms/pretui/components/drawer.usage.gts +111 -0
- package/bundled-realms/pretui/components/dropzone.gts +314 -0
- package/bundled-realms/pretui/components/dropzone.md +56 -0
- package/bundled-realms/pretui/components/dropzone.usage.gts +203 -0
- package/bundled-realms/pretui/components/dueling-picklist.gts +991 -0
- package/bundled-realms/pretui/components/dueling-picklist.md +67 -0
- package/bundled-realms/pretui/components/dueling-picklist.usage.gts +182 -0
- package/bundled-realms/pretui/components/dynamic-island.gts +264 -0
- package/bundled-realms/pretui/components/dynamic-island.md +45 -0
- package/bundled-realms/pretui/components/dynamic-island.usage.gts +195 -0
- package/bundled-realms/pretui/components/easing-curve.gts +629 -0
- package/bundled-realms/pretui/components/easing-curve.md +46 -0
- package/bundled-realms/pretui/components/easing-curve.usage.gts +122 -0
- package/bundled-realms/pretui/components/edit-in-place.gts +559 -0
- package/bundled-realms/pretui/components/edit-in-place.md +63 -0
- package/bundled-realms/pretui/components/edit-in-place.usage.gts +209 -0
- package/bundled-realms/pretui/components/editable.gts +299 -0
- package/bundled-realms/pretui/components/editable.md +79 -0
- package/bundled-realms/pretui/components/editable.usage.gts +68 -0
- package/bundled-realms/pretui/components/email-input.gts +83 -0
- package/bundled-realms/pretui/components/email-input.md +52 -0
- package/bundled-realms/pretui/components/email-input.usage.gts +90 -0
- package/bundled-realms/pretui/components/emoji-button.gts +162 -0
- package/bundled-realms/pretui/components/emoji-button.md +46 -0
- package/bundled-realms/pretui/components/emoji-button.usage.gts +174 -0
- package/bundled-realms/pretui/components/emoji-picker.gts +1396 -0
- package/bundled-realms/pretui/components/emoji-picker.md +53 -0
- package/bundled-realms/pretui/components/emoji-picker.usage.gts +235 -0
- package/bundled-realms/pretui/components/empty-state.gts +162 -0
- package/bundled-realms/pretui/components/empty-state.md +63 -0
- package/bundled-realms/pretui/components/empty-state.usage.gts +113 -0
- package/bundled-realms/pretui/components/empty.gts +2 -0
- package/bundled-realms/pretui/components/empty.md +43 -0
- package/bundled-realms/pretui/components/empty.usage.gts +33 -0
- package/bundled-realms/pretui/components/entity-display.gts +126 -0
- package/bundled-realms/pretui/components/entity-display.md +50 -0
- package/bundled-realms/pretui/components/error-summary.gts +321 -0
- package/bundled-realms/pretui/components/error-summary.md +60 -0
- package/bundled-realms/pretui/components/error-summary.usage.gts +155 -0
- package/bundled-realms/pretui/components/expression-builder.gts +1211 -0
- package/bundled-realms/pretui/components/expression-builder.md +60 -0
- package/bundled-realms/pretui/components/expression-builder.usage.gts +246 -0
- package/bundled-realms/pretui/components/fab.gts +2 -0
- package/bundled-realms/pretui/components/fab.md +38 -0
- package/bundled-realms/pretui/components/fab.usage.gts +38 -0
- package/bundled-realms/pretui/components/feature-voting.gts +365 -0
- package/bundled-realms/pretui/components/feature-voting.md +44 -0
- package/bundled-realms/pretui/components/feature-voting.usage.gts +114 -0
- package/bundled-realms/pretui/components/feed.gts +244 -0
- package/bundled-realms/pretui/components/feed.md +56 -0
- package/bundled-realms/pretui/components/feed.usage.gts +250 -0
- package/bundled-realms/pretui/components/field-error.gts +127 -0
- package/bundled-realms/pretui/components/field-error.md +60 -0
- package/bundled-realms/pretui/components/field-error.usage.gts +146 -0
- package/bundled-realms/pretui/components/field.gts +185 -0
- package/bundled-realms/pretui/components/field.md +60 -0
- package/bundled-realms/pretui/components/field.usage.gts +73 -0
- package/bundled-realms/pretui/components/fieldset.gts +124 -0
- package/bundled-realms/pretui/components/fieldset.md +65 -0
- package/bundled-realms/pretui/components/fieldset.usage.gts +105 -0
- package/bundled-realms/pretui/components/file-trigger.gts +185 -0
- package/bundled-realms/pretui/components/file-trigger.md +53 -0
- package/bundled-realms/pretui/components/file-trigger.usage.gts +121 -0
- package/bundled-realms/pretui/components/file-upload.gts +353 -0
- package/bundled-realms/pretui/components/file-upload.md +63 -0
- package/bundled-realms/pretui/components/file-upload.usage.gts +64 -0
- package/bundled-realms/pretui/components/filmstrip.gts +465 -0
- package/bundled-realms/pretui/components/filmstrip.md +53 -0
- package/bundled-realms/pretui/components/filmstrip.usage.gts +115 -0
- package/bundled-realms/pretui/components/filter-chips.gts +236 -0
- package/bundled-realms/pretui/components/filter-chips.md +58 -0
- package/bundled-realms/pretui/components/filter-chips.usage.gts +102 -0
- package/bundled-realms/pretui/components/filter-set.gts +293 -0
- package/bundled-realms/pretui/components/filter-set.md +60 -0
- package/bundled-realms/pretui/components/filter-set.usage.gts +145 -0
- package/bundled-realms/pretui/components/float-button.gts +339 -0
- package/bundled-realms/pretui/components/float-button.md +69 -0
- package/bundled-realms/pretui/components/float-button.usage.gts +134 -0
- package/bundled-realms/pretui/components/fold.gts +86 -0
- package/bundled-realms/pretui/components/fold.md +54 -0
- package/bundled-realms/pretui/components/fold.usage.gts +19 -0
- package/bundled-realms/pretui/components/form-field.gts +609 -0
- package/bundled-realms/pretui/components/form-field.md +77 -0
- package/bundled-realms/pretui/components/form-field.usage.gts +279 -0
- package/bundled-realms/pretui/components/form-footer.gts +246 -0
- package/bundled-realms/pretui/components/form-footer.md +59 -0
- package/bundled-realms/pretui/components/form-footer.usage.gts +217 -0
- package/bundled-realms/pretui/components/form-layout.gts +130 -0
- package/bundled-realms/pretui/components/form-layout.md +52 -0
- package/bundled-realms/pretui/components/form-layout.usage.gts +200 -0
- package/bundled-realms/pretui/components/form-section.gts +434 -0
- package/bundled-realms/pretui/components/form-section.md +67 -0
- package/bundled-realms/pretui/components/form-section.usage.gts +402 -0
- package/bundled-realms/pretui/components/form.gts +315 -0
- package/bundled-realms/pretui/components/form.md +82 -0
- package/bundled-realms/pretui/components/form.usage.gts +306 -0
- package/bundled-realms/pretui/components/format-bytes.gts +212 -0
- package/bundled-realms/pretui/components/format-bytes.md +47 -0
- package/bundled-realms/pretui/components/format-bytes.usage.gts +274 -0
- package/bundled-realms/pretui/components/format-date.gts +249 -0
- package/bundled-realms/pretui/components/format-date.md +55 -0
- package/bundled-realms/pretui/components/format-date.usage.gts +245 -0
- package/bundled-realms/pretui/components/format-number.gts +138 -0
- package/bundled-realms/pretui/components/format-number.md +51 -0
- package/bundled-realms/pretui/components/format-number.usage.gts +261 -0
- package/bundled-realms/pretui/components/formatted-value.gts +65 -0
- package/bundled-realms/pretui/components/freestyle-usage.gts +311 -0
- package/bundled-realms/pretui/components/freestyle-usage.md +49 -0
- package/bundled-realms/pretui/components/gallery.gts +819 -0
- package/bundled-realms/pretui/components/gallery.md +65 -0
- package/bundled-realms/pretui/components/gallery.usage.gts +212 -0
- package/bundled-realms/pretui/components/gradient-editor.gts +682 -0
- package/bundled-realms/pretui/components/gradient-editor.md +43 -0
- package/bundled-realms/pretui/components/gradient-editor.usage.gts +69 -0
- package/bundled-realms/pretui/components/graph-layout.md +54 -0
- package/bundled-realms/pretui/components/graph-layout.usage.gts +343 -0
- package/bundled-realms/pretui/components/graph-outline.gts +553 -0
- package/bundled-realms/pretui/components/graph-outline.md +50 -0
- package/bundled-realms/pretui/components/graph-outline.usage.gts +88 -0
- package/bundled-realms/pretui/components/grid.gts +68 -0
- package/bundled-realms/pretui/components/grid.md +54 -0
- package/bundled-realms/pretui/components/grid.usage.gts +144 -0
- package/bundled-realms/pretui/components/handle.gts +192 -0
- package/bundled-realms/pretui/components/handle.md +55 -0
- package/bundled-realms/pretui/components/handle.usage.gts +174 -0
- package/bundled-realms/pretui/components/hero-split.gts +307 -0
- package/bundled-realms/pretui/components/hero-split.md +50 -0
- package/bundled-realms/pretui/components/hero-split.usage.gts +200 -0
- package/bundled-realms/pretui/components/hover-actions.gts +485 -0
- package/bundled-realms/pretui/components/hover-actions.md +55 -0
- package/bundled-realms/pretui/components/hover-actions.usage.gts +224 -0
- package/bundled-realms/pretui/components/hover-card.gts +347 -0
- package/bundled-realms/pretui/components/hover-card.md +72 -0
- package/bundled-realms/pretui/components/hover-card.usage.gts +174 -0
- package/bundled-realms/pretui/components/hover-video-player.gts +315 -0
- package/bundled-realms/pretui/components/hover-video-player.md +57 -0
- package/bundled-realms/pretui/components/hover-video-player.usage.gts +152 -0
- package/bundled-realms/pretui/components/icon-button.examples.gts +69 -0
- package/bundled-realms/pretui/components/icon-button.gts +153 -0
- package/bundled-realms/pretui/components/icon-button.md +72 -0
- package/bundled-realms/pretui/components/icon-button.usage.gts +208 -0
- package/bundled-realms/pretui/components/image-cropper.gts +693 -0
- package/bundled-realms/pretui/components/image-cropper.md +52 -0
- package/bundled-realms/pretui/components/image-cropper.usage.gts +108 -0
- package/bundled-realms/pretui/components/image-frame.gts +91 -0
- package/bundled-realms/pretui/components/image-frame.md +42 -0
- package/bundled-realms/pretui/components/image-frame.usage.gts +28 -0
- package/bundled-realms/pretui/components/image.gts +326 -0
- package/bundled-realms/pretui/components/image.md +71 -0
- package/bundled-realms/pretui/components/image.usage.gts +126 -0
- package/bundled-realms/pretui/components/in-view.gts +245 -0
- package/bundled-realms/pretui/components/in-view.md +53 -0
- package/bundled-realms/pretui/components/in-view.usage.gts +264 -0
- package/bundled-realms/pretui/components/indicator.gts +137 -0
- package/bundled-realms/pretui/components/indicator.md +60 -0
- package/bundled-realms/pretui/components/indicator.usage.gts +66 -0
- package/bundled-realms/pretui/components/infinite-scroll.gts +163 -0
- package/bundled-realms/pretui/components/infinite-scroll.md +54 -0
- package/bundled-realms/pretui/components/infinite-scroll.usage.gts +73 -0
- package/bundled-realms/pretui/components/input-group.gts +264 -0
- package/bundled-realms/pretui/components/input-group.md +55 -0
- package/bundled-realms/pretui/components/input-group.usage.gts +283 -0
- package/bundled-realms/pretui/components/input.examples.gts +61 -0
- package/bundled-realms/pretui/components/input.gts +202 -0
- package/bundled-realms/pretui/components/input.md +67 -0
- package/bundled-realms/pretui/components/input.usage.gts +208 -0
- package/bundled-realms/pretui/components/interactive-input.gts +343 -0
- package/bundled-realms/pretui/components/interactive-input.md +45 -0
- package/bundled-realms/pretui/components/interactive-input.usage.gts +98 -0
- package/bundled-realms/pretui/components/item.gts +218 -0
- package/bundled-realms/pretui/components/item.md +66 -0
- package/bundled-realms/pretui/components/item.usage.gts +121 -0
- package/bundled-realms/pretui/components/joystick.gts +381 -0
- package/bundled-realms/pretui/components/joystick.md +52 -0
- package/bundled-realms/pretui/components/joystick.usage.gts +140 -0
- package/bundled-realms/pretui/components/json-editor.gts +1016 -0
- package/bundled-realms/pretui/components/json-editor.md +51 -0
- package/bundled-realms/pretui/components/json-editor.usage.gts +230 -0
- package/bundled-realms/pretui/components/json-tree.gts +596 -0
- package/bundled-realms/pretui/components/json-tree.md +52 -0
- package/bundled-realms/pretui/components/json-tree.usage.gts +217 -0
- package/bundled-realms/pretui/components/jump-chip.gts +230 -0
- package/bundled-realms/pretui/components/jump-chip.md +44 -0
- package/bundled-realms/pretui/components/jump-chip.usage.gts +151 -0
- package/bundled-realms/pretui/components/kbd.gts +60 -0
- package/bundled-realms/pretui/components/kbd.md +50 -0
- package/bundled-realms/pretui/components/kbd.usage.gts +98 -0
- package/bundled-realms/pretui/components/key-value.gts +150 -0
- package/bundled-realms/pretui/components/key-value.md +81 -0
- package/bundled-realms/pretui/components/key-value.usage.gts +125 -0
- package/bundled-realms/pretui/components/known-date.gts +770 -0
- package/bundled-realms/pretui/components/known-date.md +56 -0
- package/bundled-realms/pretui/components/known-date.usage.gts +192 -0
- package/bundled-realms/pretui/components/label.gts +60 -0
- package/bundled-realms/pretui/components/label.md +56 -0
- package/bundled-realms/pretui/components/label.usage.gts +63 -0
- package/bundled-realms/pretui/components/layer-manager.gts +1884 -0
- package/bundled-realms/pretui/components/layer-manager.md +59 -0
- package/bundled-realms/pretui/components/layer-manager.usage.gts +233 -0
- package/bundled-realms/pretui/components/lightbox.gts +461 -0
- package/bundled-realms/pretui/components/lightbox.md +57 -0
- package/bundled-realms/pretui/components/lightbox.usage.gts +180 -0
- package/bundled-realms/pretui/components/line-grid.examples.gts +156 -0
- package/bundled-realms/pretui/components/line-grid.gts +523 -0
- package/bundled-realms/pretui/components/line-grid.md +55 -0
- package/bundled-realms/pretui/components/line-grid.usage.gts +181 -0
- package/bundled-realms/pretui/components/list.gts +679 -0
- package/bundled-realms/pretui/components/list.md +90 -0
- package/bundled-realms/pretui/components/list.usage.gts +203 -0
- package/bundled-realms/pretui/components/loader.gts +2 -0
- package/bundled-realms/pretui/components/loader.md +33 -0
- package/bundled-realms/pretui/components/loader.usage.gts +35 -0
- package/bundled-realms/pretui/components/loading-overlay.gts +120 -0
- package/bundled-realms/pretui/components/loading-overlay.md +68 -0
- package/bundled-realms/pretui/components/loading-overlay.usage.gts +102 -0
- package/bundled-realms/pretui/components/loading-state.gts +124 -0
- package/bundled-realms/pretui/components/loading-state.md +48 -0
- package/bundled-realms/pretui/components/loading-state.usage.gts +16 -0
- package/bundled-realms/pretui/components/lookup.gts +451 -0
- package/bundled-realms/pretui/components/lookup.md +59 -0
- package/bundled-realms/pretui/components/lookup.usage.gts +235 -0
- package/bundled-realms/pretui/components/magnetic.gts +158 -0
- package/bundled-realms/pretui/components/magnetic.md +48 -0
- package/bundled-realms/pretui/components/magnetic.usage.gts +125 -0
- package/bundled-realms/pretui/components/marquee.gts +141 -0
- package/bundled-realms/pretui/components/marquee.md +43 -0
- package/bundled-realms/pretui/components/marquee.usage.gts +116 -0
- package/bundled-realms/pretui/components/masonry.gts +95 -0
- package/bundled-realms/pretui/components/masonry.md +59 -0
- package/bundled-realms/pretui/components/masonry.usage.gts +238 -0
- package/bundled-realms/pretui/components/media-inspector.gts +145 -0
- package/bundled-realms/pretui/components/media-inspector.md +46 -0
- package/bundled-realms/pretui/components/media-inspector.usage.gts +83 -0
- package/bundled-realms/pretui/components/media-player.gts +1133 -0
- package/bundled-realms/pretui/components/media-player.md +74 -0
- package/bundled-realms/pretui/components/media-player.usage.gts +265 -0
- package/bundled-realms/pretui/components/media-viewer.gts +136 -0
- package/bundled-realms/pretui/components/media-viewer.md +47 -0
- package/bundled-realms/pretui/components/media-viewer.usage.gts +70 -0
- package/bundled-realms/pretui/components/mentions.gts +340 -0
- package/bundled-realms/pretui/components/mentions.md +65 -0
- package/bundled-realms/pretui/components/mentions.usage.gts +73 -0
- package/bundled-realms/pretui/components/menu-panel.gts +367 -0
- package/bundled-realms/pretui/components/menu.gts +652 -0
- package/bundled-realms/pretui/components/menu.md +69 -0
- package/bundled-realms/pretui/components/menu.usage.gts +116 -0
- package/bundled-realms/pretui/components/menubar.gts +1200 -0
- package/bundled-realms/pretui/components/menubar.md +49 -0
- package/bundled-realms/pretui/components/menubar.usage.gts +220 -0
- package/bundled-realms/pretui/components/meter.gts +113 -0
- package/bundled-realms/pretui/components/meter.md +50 -0
- package/bundled-realms/pretui/components/meter.usage.gts +50 -0
- package/bundled-realms/pretui/components/modal.gts +2 -0
- package/bundled-realms/pretui/components/modal.md +41 -0
- package/bundled-realms/pretui/components/modal.usage.gts +50 -0
- package/bundled-realms/pretui/components/model-viewer.gts +658 -0
- package/bundled-realms/pretui/components/model-viewer.md +57 -0
- package/bundled-realms/pretui/components/model-viewer.usage.gts +123 -0
- package/bundled-realms/pretui/components/money-input.gts +66 -0
- package/bundled-realms/pretui/components/money-input.md +43 -0
- package/bundled-realms/pretui/components/money-input.usage.gts +293 -0
- package/bundled-realms/pretui/components/morphing-dialog.gts +173 -0
- package/bundled-realms/pretui/components/morphing-dialog.md +46 -0
- package/bundled-realms/pretui/components/morphing-dialog.usage.gts +168 -0
- package/bundled-realms/pretui/components/morphing-popover.gts +245 -0
- package/bundled-realms/pretui/components/morphing-popover.md +47 -0
- package/bundled-realms/pretui/components/morphing-popover.usage.gts +176 -0
- package/bundled-realms/pretui/components/multi-select.gts +392 -0
- package/bundled-realms/pretui/components/multi-select.md +57 -0
- package/bundled-realms/pretui/components/multi-select.usage.gts +97 -0
- package/bundled-realms/pretui/components/native-select.gts +212 -0
- package/bundled-realms/pretui/components/native-select.md +77 -0
- package/bundled-realms/pretui/components/native-select.usage.gts +109 -0
- package/bundled-realms/pretui/components/navigation-menu.gts +351 -0
- package/bundled-realms/pretui/components/navigation-menu.md +65 -0
- package/bundled-realms/pretui/components/navigation-menu.usage.gts +61 -0
- package/bundled-realms/pretui/components/node-canvas.gts +1174 -0
- package/bundled-realms/pretui/components/node-canvas.md +57 -0
- package/bundled-realms/pretui/components/node-canvas.usage.gts +548 -0
- package/bundled-realms/pretui/components/node-card.gts +181 -0
- package/bundled-realms/pretui/components/node-card.md +53 -0
- package/bundled-realms/pretui/components/node-card.usage.gts +156 -0
- package/bundled-realms/pretui/components/notification.gts +242 -0
- package/bundled-realms/pretui/components/notification.md +69 -0
- package/bundled-realms/pretui/components/notification.usage.gts +125 -0
- package/bundled-realms/pretui/components/number-input.gts +191 -0
- package/bundled-realms/pretui/components/number-input.md +54 -0
- package/bundled-realms/pretui/components/number-input.usage.gts +132 -0
- package/bundled-realms/pretui/components/odometer.gts +349 -0
- package/bundled-realms/pretui/components/odometer.md +55 -0
- package/bundled-realms/pretui/components/odometer.usage.gts +258 -0
- package/bundled-realms/pretui/components/onboarding.gts +298 -0
- package/bundled-realms/pretui/components/onboarding.md +48 -0
- package/bundled-realms/pretui/components/onboarding.usage.gts +155 -0
- package/bundled-realms/pretui/components/origin-grid.gts +486 -0
- package/bundled-realms/pretui/components/origin-grid.md +47 -0
- package/bundled-realms/pretui/components/origin-grid.usage.gts +87 -0
- package/bundled-realms/pretui/components/otp-input.gts +289 -0
- package/bundled-realms/pretui/components/otp-input.md +47 -0
- package/bundled-realms/pretui/components/otp-input.usage.gts +131 -0
- package/bundled-realms/pretui/components/page-scaffold.gts +343 -0
- package/bundled-realms/pretui/components/page-scaffold.md +57 -0
- package/bundled-realms/pretui/components/page-scaffold.usage.gts +391 -0
- package/bundled-realms/pretui/components/pagination.gts +150 -0
- package/bundled-realms/pretui/components/pagination.md +56 -0
- package/bundled-realms/pretui/components/pagination.usage.gts +33 -0
- package/bundled-realms/pretui/components/panel-section.gts +301 -0
- package/bundled-realms/pretui/components/panel-section.md +48 -0
- package/bundled-realms/pretui/components/panel-section.usage.gts +137 -0
- package/bundled-realms/pretui/components/panel.gts +158 -0
- package/bundled-realms/pretui/components/panel.md +58 -0
- package/bundled-realms/pretui/components/panel.usage.gts +82 -0
- package/bundled-realms/pretui/components/password-input.gts +329 -0
- package/bundled-realms/pretui/components/password-input.md +58 -0
- package/bundled-realms/pretui/components/password-input.usage.gts +156 -0
- package/bundled-realms/pretui/components/password-strength.gts +485 -0
- package/bundled-realms/pretui/components/password-strength.md +50 -0
- package/bundled-realms/pretui/components/password-strength.usage.gts +34 -0
- package/bundled-realms/pretui/components/path-text.gts +288 -0
- package/bundled-realms/pretui/components/path-text.md +58 -0
- package/bundled-realms/pretui/components/path-text.usage.gts +239 -0
- package/bundled-realms/pretui/components/period-input.gts +1100 -0
- package/bundled-realms/pretui/components/period-input.md +54 -0
- package/bundled-realms/pretui/components/period-input.usage.gts +299 -0
- package/bundled-realms/pretui/components/phone-input.gts +80 -0
- package/bundled-realms/pretui/components/phone-input.md +55 -0
- package/bundled-realms/pretui/components/phone-input.usage.gts +91 -0
- package/bundled-realms/pretui/components/pin-input.gts +2 -0
- package/bundled-realms/pretui/components/pin-input.md +45 -0
- package/bundled-realms/pretui/components/pin-input.usage.gts +39 -0
- package/bundled-realms/pretui/components/pip-scale.gts +717 -0
- package/bundled-realms/pretui/components/pip-scale.md +55 -0
- package/bundled-realms/pretui/components/pip-scale.usage.gts +318 -0
- package/bundled-realms/pretui/components/popconfirm.gts +380 -0
- package/bundled-realms/pretui/components/popconfirm.md +78 -0
- package/bundled-realms/pretui/components/popconfirm.usage.gts +171 -0
- package/bundled-realms/pretui/components/popover.gts +198 -0
- package/bundled-realms/pretui/components/popover.md +57 -0
- package/bundled-realms/pretui/components/popover.usage.gts +106 -0
- package/bundled-realms/pretui/components/popup.gts +85 -0
- package/bundled-realms/pretui/components/popup.md +42 -0
- package/bundled-realms/pretui/components/popup.usage.gts +23 -0
- package/bundled-realms/pretui/components/presence.gts +156 -0
- package/bundled-realms/pretui/components/presence.md +55 -0
- package/bundled-realms/pretui/components/presence.usage.gts +204 -0
- package/bundled-realms/pretui/components/progress-bar.gts +177 -0
- package/bundled-realms/pretui/components/progress-bar.md +63 -0
- package/bundled-realms/pretui/components/progress-bar.usage.gts +172 -0
- package/bundled-realms/pretui/components/progress-radial.gts +67 -0
- package/bundled-realms/pretui/components/progress-radial.md +55 -0
- package/bundled-realms/pretui/components/progress-radial.usage.gts +76 -0
- package/bundled-realms/pretui/components/prompt-library.gts +358 -0
- package/bundled-realms/pretui/components/prompt-library.md +45 -0
- package/bundled-realms/pretui/components/prompt-library.usage.gts +117 -0
- package/bundled-realms/pretui/components/property-row.gts +316 -0
- package/bundled-realms/pretui/components/property-row.md +55 -0
- package/bundled-realms/pretui/components/property-row.usage.gts +146 -0
- package/bundled-realms/pretui/components/property-sheet.gts +133 -0
- package/bundled-realms/pretui/components/property-sheet.md +48 -0
- package/bundled-realms/pretui/components/property-sheet.usage.gts +132 -0
- package/bundled-realms/pretui/components/prose.gts +31 -0
- package/bundled-realms/pretui/components/prose.md +57 -0
- package/bundled-realms/pretui/components/prose.usage.gts +15 -0
- package/bundled-realms/pretui/components/pulsing-border.gts +297 -0
- package/bundled-realms/pretui/components/pulsing-border.md +65 -0
- package/bundled-realms/pretui/components/pulsing-border.usage.gts +253 -0
- package/bundled-realms/pretui/components/qr-code.gts +660 -0
- package/bundled-realms/pretui/components/qr-code.md +58 -0
- package/bundled-realms/pretui/components/qr-code.usage.gts +224 -0
- package/bundled-realms/pretui/components/queued-message.gts +83 -0
- package/bundled-realms/pretui/components/queued-message.md +53 -0
- package/bundled-realms/pretui/components/queued-message.usage.gts +21 -0
- package/bundled-realms/pretui/components/radio-card.gts +88 -0
- package/bundled-realms/pretui/components/radio-card.md +72 -0
- package/bundled-realms/pretui/components/radio-card.usage.gts +148 -0
- package/bundled-realms/pretui/components/radio-group.gts +119 -0
- package/bundled-realms/pretui/components/radio-group.md +46 -0
- package/bundled-realms/pretui/components/radio-group.usage.gts +80 -0
- package/bundled-realms/pretui/components/range-slider.gts +34 -0
- package/bundled-realms/pretui/components/range-slider.md +47 -0
- package/bundled-realms/pretui/components/range-slider.usage.gts +46 -0
- package/bundled-realms/pretui/components/rating.examples.gts +67 -0
- package/bundled-realms/pretui/components/rating.gts +266 -0
- package/bundled-realms/pretui/components/rating.md +55 -0
- package/bundled-realms/pretui/components/rating.usage.gts +96 -0
- package/bundled-realms/pretui/components/readiness-panel.gts +856 -0
- package/bundled-realms/pretui/components/readiness-panel.md +64 -0
- package/bundled-realms/pretui/components/readiness-panel.usage.gts +372 -0
- package/bundled-realms/pretui/components/recommendation.gts +316 -0
- package/bundled-realms/pretui/components/recommendation.md +49 -0
- package/bundled-realms/pretui/components/recommendation.usage.gts +103 -0
- package/bundled-realms/pretui/components/record-detail.gts +1170 -0
- package/bundled-realms/pretui/components/record-detail.md +63 -0
- package/bundled-realms/pretui/components/record-detail.usage.gts +533 -0
- package/bundled-realms/pretui/components/record-face.gts +153 -0
- package/bundled-realms/pretui/components/record-pill.gts +116 -0
- package/bundled-realms/pretui/components/record-pill.md +55 -0
- package/bundled-realms/pretui/components/record-pill.usage.gts +108 -0
- package/bundled-realms/pretui/components/relative-time.gts +88 -0
- package/bundled-realms/pretui/components/relative-time.md +58 -0
- package/bundled-realms/pretui/components/relative-time.usage.gts +89 -0
- package/bundled-realms/pretui/components/reorder.gts +620 -0
- package/bundled-realms/pretui/components/reorder.md +47 -0
- package/bundled-realms/pretui/components/reorder.usage.gts +171 -0
- package/bundled-realms/pretui/components/resizable.gts +2 -0
- package/bundled-realms/pretui/components/resizable.md +37 -0
- package/bundled-realms/pretui/components/resizable.usage.gts +53 -0
- package/bundled-realms/pretui/components/result-card.gts +68 -0
- package/bundled-realms/pretui/components/result-card.md +45 -0
- package/bundled-realms/pretui/components/result-card.usage.gts +29 -0
- package/bundled-realms/pretui/components/result.gts +204 -0
- package/bundled-realms/pretui/components/result.md +67 -0
- package/bundled-realms/pretui/components/result.usage.gts +85 -0
- package/bundled-realms/pretui/components/rule-row.gts +462 -0
- package/bundled-realms/pretui/components/rule-row.md +60 -0
- package/bundled-realms/pretui/components/rule-row.usage.gts +157 -0
- package/bundled-realms/pretui/components/scroll-area.gts +121 -0
- package/bundled-realms/pretui/components/scroll-area.md +59 -0
- package/bundled-realms/pretui/components/scroll-area.usage.gts +175 -0
- package/bundled-realms/pretui/components/scroll-progress.gts +222 -0
- package/bundled-realms/pretui/components/scroll-progress.md +48 -0
- package/bundled-realms/pretui/components/scroll-progress.usage.gts +162 -0
- package/bundled-realms/pretui/components/scroller.gts +323 -0
- package/bundled-realms/pretui/components/scroller.md +44 -0
- package/bundled-realms/pretui/components/scroller.usage.gts +184 -0
- package/bundled-realms/pretui/components/scrub-input.gts +691 -0
- package/bundled-realms/pretui/components/scrub-input.md +61 -0
- package/bundled-realms/pretui/components/scrub-input.usage.gts +275 -0
- package/bundled-realms/pretui/components/search-input.gts +141 -0
- package/bundled-realms/pretui/components/search-input.md +56 -0
- package/bundled-realms/pretui/components/search-input.usage.gts +91 -0
- package/bundled-realms/pretui/components/segmented-control.gts +198 -0
- package/bundled-realms/pretui/components/segmented-control.md +39 -0
- package/bundled-realms/pretui/components/segmented-control.usage.gts +66 -0
- package/bundled-realms/pretui/components/select.examples.gts +80 -0
- package/bundled-realms/pretui/components/select.gts +481 -0
- package/bundled-realms/pretui/components/select.md +71 -0
- package/bundled-realms/pretui/components/select.usage.gts +109 -0
- package/bundled-realms/pretui/components/separator.gts +2 -0
- package/bundled-realms/pretui/components/separator.md +36 -0
- package/bundled-realms/pretui/components/separator.usage.gts +32 -0
- package/bundled-realms/pretui/components/session-prep.gts +164 -0
- package/bundled-realms/pretui/components/session-prep.md +48 -0
- package/bundled-realms/pretui/components/session-prep.usage.gts +122 -0
- package/bundled-realms/pretui/components/sheet-toolbar.gts +130 -0
- package/bundled-realms/pretui/components/sheet-toolbar.md +51 -0
- package/bundled-realms/pretui/components/sheet-toolbar.usage.gts +151 -0
- package/bundled-realms/pretui/components/sheet.gts +1166 -0
- package/bundled-realms/pretui/components/sheet.md +78 -0
- package/bundled-realms/pretui/components/sheet.usage.gts +318 -0
- package/bundled-realms/pretui/components/sidebar.gts +1165 -0
- package/bundled-realms/pretui/components/sidebar.md +88 -0
- package/bundled-realms/pretui/components/sidebar.usage.gts +360 -0
- package/bundled-realms/pretui/components/signature-pad.gts +840 -0
- package/bundled-realms/pretui/components/signature-pad.md +57 -0
- package/bundled-realms/pretui/components/signature-pad.usage.gts +222 -0
- package/bundled-realms/pretui/components/skeleton.gts +46 -0
- package/bundled-realms/pretui/components/skeleton.md +56 -0
- package/bundled-realms/pretui/components/skeleton.usage.gts +56 -0
- package/bundled-realms/pretui/components/skip-link.gts +65 -0
- package/bundled-realms/pretui/components/skip-link.md +49 -0
- package/bundled-realms/pretui/components/skip-link.usage.gts +47 -0
- package/bundled-realms/pretui/components/slide-over.gts +2 -0
- package/bundled-realms/pretui/components/slide-over.md +41 -0
- package/bundled-realms/pretui/components/slide-over.usage.gts +50 -0
- package/bundled-realms/pretui/components/slider.examples.gts +61 -0
- package/bundled-realms/pretui/components/slider.gts +331 -0
- package/bundled-realms/pretui/components/slider.md +59 -0
- package/bundled-realms/pretui/components/slider.usage.gts +99 -0
- package/bundled-realms/pretui/components/sliding-highlight.gts +249 -0
- package/bundled-realms/pretui/components/sliding-highlight.md +58 -0
- package/bundled-realms/pretui/components/sliding-highlight.usage.gts +184 -0
- package/bundled-realms/pretui/components/snackbar.gts +2 -0
- package/bundled-realms/pretui/components/snackbar.md +34 -0
- package/bundled-realms/pretui/components/snackbar.usage.gts +31 -0
- package/bundled-realms/pretui/components/sonner.gts +2 -0
- package/bundled-realms/pretui/components/sonner.md +42 -0
- package/bundled-realms/pretui/components/sonner.usage.gts +43 -0
- package/bundled-realms/pretui/components/sparkline.gts +233 -0
- package/bundled-realms/pretui/components/sparkline.md +58 -0
- package/bundled-realms/pretui/components/sparkline.usage.gts +74 -0
- package/bundled-realms/pretui/components/spinner.gts +57 -0
- package/bundled-realms/pretui/components/spinner.md +49 -0
- package/bundled-realms/pretui/components/spinner.usage.gts +65 -0
- package/bundled-realms/pretui/components/split-panes.gts +69 -0
- package/bundled-realms/pretui/components/split-panes.md +54 -0
- package/bundled-realms/pretui/components/split-panes.usage.gts +171 -0
- package/bundled-realms/pretui/components/spotlight.gts +174 -0
- package/bundled-realms/pretui/components/spotlight.md +51 -0
- package/bundled-realms/pretui/components/spotlight.usage.gts +197 -0
- package/bundled-realms/pretui/components/stack-divider.gts +58 -0
- package/bundled-realms/pretui/components/stack.gts +284 -0
- package/bundled-realms/pretui/components/stack.md +81 -0
- package/bundled-realms/pretui/components/stack.usage.gts +228 -0
- package/bundled-realms/pretui/components/stacking-cards.gts +137 -0
- package/bundled-realms/pretui/components/stacking-cards.md +40 -0
- package/bundled-realms/pretui/components/stacking-cards.usage.gts +124 -0
- package/bundled-realms/pretui/components/stat.gts +216 -0
- package/bundled-realms/pretui/components/stat.md +57 -0
- package/bundled-realms/pretui/components/stat.usage.gts +16 -0
- package/bundled-realms/pretui/components/statement-table.examples.gts +185 -0
- package/bundled-realms/pretui/components/statement-table.gts +792 -0
- package/bundled-realms/pretui/components/statement-table.md +58 -0
- package/bundled-realms/pretui/components/statement-table.usage.gts +252 -0
- package/bundled-realms/pretui/components/status-chip.gts +37 -0
- package/bundled-realms/pretui/components/status-chip.md +48 -0
- package/bundled-realms/pretui/components/status-chip.usage.gts +36 -0
- package/bundled-realms/pretui/components/step-list.gts +624 -0
- package/bundled-realms/pretui/components/step-list.md +50 -0
- package/bundled-realms/pretui/components/step-list.usage.gts +166 -0
- package/bundled-realms/pretui/components/stepper.gts +201 -0
- package/bundled-realms/pretui/components/stepper.md +54 -0
- package/bundled-realms/pretui/components/stepper.usage.gts +103 -0
- package/bundled-realms/pretui/components/steps-with-media.gts +254 -0
- package/bundled-realms/pretui/components/steps-with-media.md +54 -0
- package/bundled-realms/pretui/components/steps-with-media.usage.gts +208 -0
- package/bundled-realms/pretui/components/streaming-text.gts +77 -0
- package/bundled-realms/pretui/components/streaming-text.md +55 -0
- package/bundled-realms/pretui/components/streaming-text.usage.gts +15 -0
- package/bundled-realms/pretui/components/swatch-chip.gts +104 -0
- package/bundled-realms/pretui/components/swatch-chip.md +47 -0
- package/bundled-realms/pretui/components/swatch-chip.usage.gts +21 -0
- package/bundled-realms/pretui/components/swatch.gts +141 -0
- package/bundled-realms/pretui/components/swatch.md +50 -0
- package/bundled-realms/pretui/components/swatch.usage.gts +80 -0
- package/bundled-realms/pretui/components/switch.examples.gts +53 -0
- package/bundled-realms/pretui/components/switch.gts +118 -0
- package/bundled-realms/pretui/components/switch.md +62 -0
- package/bundled-realms/pretui/components/switch.usage.gts +59 -0
- package/bundled-realms/pretui/components/table-of-contents.gts +384 -0
- package/bundled-realms/pretui/components/table-of-contents.md +75 -0
- package/bundled-realms/pretui/components/table-of-contents.usage.gts +270 -0
- package/bundled-realms/pretui/components/table.gts +158 -0
- package/bundled-realms/pretui/components/table.md +80 -0
- package/bundled-realms/pretui/components/table.usage.gts +44 -0
- package/bundled-realms/pretui/components/tabs.gts +117 -0
- package/bundled-realms/pretui/components/tabs.md +58 -0
- package/bundled-realms/pretui/components/tabs.usage.gts +94 -0
- package/bundled-realms/pretui/components/tags-input.gts +52 -0
- package/bundled-realms/pretui/components/tags-input.md +48 -0
- package/bundled-realms/pretui/components/tags-input.usage.gts +56 -0
- package/bundled-realms/pretui/components/task-row.gts +483 -0
- package/bundled-realms/pretui/components/task-row.md +48 -0
- package/bundled-realms/pretui/components/task-row.usage.gts +119 -0
- package/bundled-realms/pretui/components/terminal-animation.gts +355 -0
- package/bundled-realms/pretui/components/terminal-animation.md +49 -0
- package/bundled-realms/pretui/components/terminal-animation.usage.gts +110 -0
- package/bundled-realms/pretui/components/text-effects.gts +339 -0
- package/bundled-realms/pretui/components/text-effects.md +49 -0
- package/bundled-realms/pretui/components/text-effects.usage.gts +192 -0
- package/bundled-realms/pretui/components/text-morph.gts +292 -0
- package/bundled-realms/pretui/components/text-morph.md +45 -0
- package/bundled-realms/pretui/components/text-morph.usage.gts +117 -0
- package/bundled-realms/pretui/components/text-rotate.gts +121 -0
- package/bundled-realms/pretui/components/text-rotate.md +44 -0
- package/bundled-realms/pretui/components/text-rotate.usage.gts +73 -0
- package/bundled-realms/pretui/components/text-scramble.gts +189 -0
- package/bundled-realms/pretui/components/text-scramble.md +42 -0
- package/bundled-realms/pretui/components/text-scramble.usage.gts +80 -0
- package/bundled-realms/pretui/components/text-shimmer.gts +89 -0
- package/bundled-realms/pretui/components/text-shimmer.md +42 -0
- package/bundled-realms/pretui/components/text-shimmer.usage.gts +69 -0
- package/bundled-realms/pretui/components/textarea.gts +112 -0
- package/bundled-realms/pretui/components/textarea.md +51 -0
- package/bundled-realms/pretui/components/textarea.usage.gts +100 -0
- package/bundled-realms/pretui/components/theme-frame.gts +272 -0
- package/bundled-realms/pretui/components/theme-frame.md +48 -0
- package/bundled-realms/pretui/components/thinking.gts +431 -0
- package/bundled-realms/pretui/components/thinking.md +47 -0
- package/bundled-realms/pretui/components/thinking.usage.gts +134 -0
- package/bundled-realms/pretui/components/tilt.gts +194 -0
- package/bundled-realms/pretui/components/tilt.md +50 -0
- package/bundled-realms/pretui/components/tilt.usage.gts +174 -0
- package/bundled-realms/pretui/components/time-input.gts +583 -0
- package/bundled-realms/pretui/components/time-input.md +51 -0
- package/bundled-realms/pretui/components/time-input.usage.gts +110 -0
- package/bundled-realms/pretui/components/timeline.gts +269 -0
- package/bundled-realms/pretui/components/timeline.md +63 -0
- package/bundled-realms/pretui/components/timeline.usage.gts +180 -0
- package/bundled-realms/pretui/components/toast.gts +85 -0
- package/bundled-realms/pretui/components/toast.md +57 -0
- package/bundled-realms/pretui/components/toast.usage.gts +18 -0
- package/bundled-realms/pretui/components/toaster.gts +808 -0
- package/bundled-realms/pretui/components/toaster.md +83 -0
- package/bundled-realms/pretui/components/toaster.usage.gts +256 -0
- package/bundled-realms/pretui/components/toggle-group.gts +435 -0
- package/bundled-realms/pretui/components/toggle-group.md +59 -0
- package/bundled-realms/pretui/components/toggle-group.usage.gts +260 -0
- package/bundled-realms/pretui/components/toggle-matrix.gts +994 -0
- package/bundled-realms/pretui/components/toggle-matrix.md +44 -0
- package/bundled-realms/pretui/components/toggle-matrix.usage.gts +243 -0
- package/bundled-realms/pretui/components/toggle.gts +235 -0
- package/bundled-realms/pretui/components/toggle.md +59 -0
- package/bundled-realms/pretui/components/toggle.usage.gts +244 -0
- package/bundled-realms/pretui/components/token-input.gts +483 -0
- package/bundled-realms/pretui/components/token-input.md +50 -0
- package/bundled-realms/pretui/components/token-input.usage.gts +137 -0
- package/bundled-realms/pretui/components/token.gts +197 -0
- package/bundled-realms/pretui/components/token.md +63 -0
- package/bundled-realms/pretui/components/token.usage.gts +128 -0
- package/bundled-realms/pretui/components/toolbar.gts +62 -0
- package/bundled-realms/pretui/components/toolbar.md +50 -0
- package/bundled-realms/pretui/components/toolbar.usage.gts +78 -0
- package/bundled-realms/pretui/components/tooltip.gts +212 -0
- package/bundled-realms/pretui/components/tooltip.md +53 -0
- package/bundled-realms/pretui/components/tooltip.usage.gts +64 -0
- package/bundled-realms/pretui/components/tour.gts +436 -0
- package/bundled-realms/pretui/components/tour.md +62 -0
- package/bundled-realms/pretui/components/tour.usage.gts +70 -0
- package/bundled-realms/pretui/components/transfer.gts +2 -0
- package/bundled-realms/pretui/components/transfer.md +39 -0
- package/bundled-realms/pretui/components/transfer.usage.gts +52 -0
- package/bundled-realms/pretui/components/transmute.gts +103 -0
- package/bundled-realms/pretui/components/transmute.md +49 -0
- package/bundled-realms/pretui/components/transmute.usage.gts +16 -0
- package/bundled-realms/pretui/components/tree-select.gts +596 -0
- package/bundled-realms/pretui/components/tree-select.md +70 -0
- package/bundled-realms/pretui/components/tree-select.usage.gts +75 -0
- package/bundled-realms/pretui/components/tree.gts +532 -0
- package/bundled-realms/pretui/components/tree.md +68 -0
- package/bundled-realms/pretui/components/tree.usage.gts +278 -0
- package/bundled-realms/pretui/components/trim-bar.gts +611 -0
- package/bundled-realms/pretui/components/trim-bar.md +48 -0
- package/bundled-realms/pretui/components/trim-bar.usage.gts +112 -0
- package/bundled-realms/pretui/components/typewriter.gts +122 -0
- package/bundled-realms/pretui/components/typewriter.md +45 -0
- package/bundled-realms/pretui/components/typewriter.usage.gts +105 -0
- package/bundled-realms/pretui/components/typography.gts +379 -0
- package/bundled-realms/pretui/components/typography.md +77 -0
- package/bundled-realms/pretui/components/typography.usage.gts +52 -0
- package/bundled-realms/pretui/components/url-input.gts +130 -0
- package/bundled-realms/pretui/components/url-input.md +58 -0
- package/bundled-realms/pretui/components/url-input.usage.gts +97 -0
- package/bundled-realms/pretui/components/usage-action.gts +16 -0
- package/bundled-realms/pretui/components/usage-argument.gts +109 -0
- package/bundled-realms/pretui/components/usage-argument.md +44 -0
- package/bundled-realms/pretui/components/usage-array.gts +80 -0
- package/bundled-realms/pretui/components/usage-array.md +47 -0
- package/bundled-realms/pretui/components/usage-bool.gts +62 -0
- package/bundled-realms/pretui/components/usage-bool.md +47 -0
- package/bundled-realms/pretui/components/usage-component-arg.gts +16 -0
- package/bundled-realms/pretui/components/usage-css-variable.gts +56 -0
- package/bundled-realms/pretui/components/usage-css-variable.md +48 -0
- package/bundled-realms/pretui/components/usage-number.gts +127 -0
- package/bundled-realms/pretui/components/usage-number.md +48 -0
- package/bundled-realms/pretui/components/usage-object.gts +58 -0
- package/bundled-realms/pretui/components/usage-object.md +48 -0
- package/bundled-realms/pretui/components/usage-string.gts +89 -0
- package/bundled-realms/pretui/components/usage-string.md +46 -0
- package/bundled-realms/pretui/components/usage-yield.gts +16 -0
- package/bundled-realms/pretui/components/value-input.gts +265 -0
- package/bundled-realms/pretui/components/value-input.md +49 -0
- package/bundled-realms/pretui/components/value-input.usage.gts +176 -0
- package/bundled-realms/pretui/components/video-player.gts +80 -0
- package/bundled-realms/pretui/components/video-player.md +46 -0
- package/bundled-realms/pretui/components/video-player.usage.gts +107 -0
- package/bundled-realms/pretui/components/viewport.gts +520 -0
- package/bundled-realms/pretui/components/viewport.md +55 -0
- package/bundled-realms/pretui/components/visually-hidden.gts +49 -0
- package/bundled-realms/pretui/components/visually-hidden.md +47 -0
- package/bundled-realms/pretui/components/visually-hidden.usage.gts +27 -0
- package/bundled-realms/pretui/components/watermark.gts +141 -0
- package/bundled-realms/pretui/components/watermark.md +58 -0
- package/bundled-realms/pretui/components/watermark.usage.gts +63 -0
- package/bundled-realms/pretui/components/waveform.gts +376 -0
- package/bundled-realms/pretui/components/waveform.md +56 -0
- package/bundled-realms/pretui/components/waveform.usage.gts +131 -0
- package/bundled-realms/pretui/components/wizard.gts +677 -0
- package/bundled-realms/pretui/components/wizard.md +58 -0
- package/bundled-realms/pretui/components/wizard.usage.gts +188 -0
- package/bundled-realms/pretui/components/work-item.gts +207 -0
- package/bundled-realms/pretui/components/work-item.md +58 -0
- package/bundled-realms/pretui/components/work-item.usage.gts +103 -0
- package/bundled-realms/pretui/components/zoomable-frame.gts +541 -0
- package/bundled-realms/pretui/components/zoomable-frame.md +50 -0
- package/bundled-realms/pretui/components/zoomable-frame.usage.gts +175 -0
- package/bundled-realms/pretui/cropper/LICENSE +21 -0
- package/bundled-realms/pretui/cropper/README.md +92 -0
- package/bundled-realms/pretui/cropper/index.js +81 -0
- package/bundled-realms/pretui/data-component.gts +1161 -0
- package/bundled-realms/pretui/demo-design-layers.gts +112 -0
- package/bundled-realms/pretui/demo-design-tools.gts +463 -0
- package/bundled-realms/pretui/demo-design-value.gts +9 -0
- package/bundled-realms/pretui/demo-forms-core.gts +82 -0
- package/bundled-realms/pretui/demo-forms-expression.gts +76 -0
- package/bundled-realms/pretui/demo-forms-record.gts +16 -0
- package/bundled-realms/pretui/demo-foundations-more.gts +10 -0
- package/bundled-realms/pretui/demo-foundations.gts +8 -0
- package/bundled-realms/pretui/demo-ink-feedback.gts +12 -0
- package/bundled-realms/pretui/demo-json.gts +17 -0
- package/bundled-realms/pretui/demo-locations.ts +103 -0
- package/bundled-realms/pretui/demo-media-library.gts +101 -0
- package/bundled-realms/pretui/demo-media.gts +6 -0
- package/bundled-realms/pretui/demo-menu.gts +182 -0
- package/bundled-realms/pretui/demo-motion-core.gts +4 -0
- package/bundled-realms/pretui/demo-motion-pointer.gts +21 -0
- package/bundled-realms/pretui/demo-reading-format.gts +10 -0
- package/bundled-realms/pretui/demo-structure-dashboard.gts +212 -0
- package/bundled-realms/pretui/demo-structure-data.gts +4 -0
- package/bundled-realms/pretui/demo-structure-scroll.gts +19 -0
- package/bundled-realms/pretui/demo-structure.gts +21 -0
- package/bundled-realms/pretui/demo-surfaces-grid.gts +104 -0
- package/bundled-realms/pretui/demo-texture.gts +21 -0
- package/bundled-realms/pretui/docs/catalog-taxonomy.md +200 -0
- package/bundled-realms/pretui/docs/composites.md +13 -0
- package/bundled-realms/pretui/docs/reading.md +12 -0
- package/bundled-realms/pretui/docs/structure.md +8 -0
- package/bundled-realms/pretui/emoji-data/LICENSE +202 -0
- package/bundled-realms/pretui/emoji-data/LICENSE.data +202 -0
- package/bundled-realms/pretui/emoji-data/LICENSE.emojibase +21 -0
- package/bundled-realms/pretui/emoji-data/LICENSE.unicode +39 -0
- package/bundled-realms/pretui/emoji-data/README.md +360 -0
- package/bundled-realms/pretui/emoji-data/data.ts +45 -0
- package/bundled-realms/pretui/emoji-data/engine.ts +465 -0
- package/bundled-realms/pretui/example-gallery.gts +129 -0
- package/bundled-realms/pretui/examples-kit.gts +228 -0
- package/bundled-realms/pretui/examples-listing.gts +51 -0
- package/bundled-realms/pretui/examples.gts +584 -0
- package/bundled-realms/pretui/focus.gts +459 -0
- package/bundled-realms/pretui/gridstack/LICENSE +21 -0
- package/bundled-realms/pretui/gridstack/README.md +103 -0
- package/bundled-realms/pretui/gridstack/index.js +169 -0
- package/bundled-realms/pretui/icon-registry.gts +753 -0
- package/bundled-realms/pretui/index.json +11 -0
- package/bundled-realms/pretui/internal/agentic-chat.gts +101 -0
- package/bundled-realms/pretui/internal/blocks.gts +35 -0
- package/bundled-realms/pretui/internal/choice-cards.gts +258 -0
- package/bundled-realms/pretui/internal/color-fixtures.gts +3 -0
- package/bundled-realms/pretui/internal/composites-fixtures.gts +16 -0
- package/bundled-realms/pretui/internal/corner-mark.gts +83 -0
- package/bundled-realms/pretui/internal/design-tools.gts +881 -0
- package/bundled-realms/pretui/internal/extras.gts +31 -0
- package/bundled-realms/pretui/internal/feedback.gts +27 -0
- package/bundled-realms/pretui/internal/file-intake-fixtures.gts +17 -0
- package/bundled-realms/pretui/internal/file-intake.gts +193 -0
- package/bundled-realms/pretui/internal/forms-core.gts +394 -0
- package/bundled-realms/pretui/internal/forms-expression.gts +837 -0
- package/bundled-realms/pretui/internal/forms-picker-fixtures.gts +10 -0
- package/bundled-realms/pretui/internal/forms-picker.gts +129 -0
- package/bundled-realms/pretui/internal/forms-record.gts +173 -0
- package/bundled-realms/pretui/internal/freestyle.gts +117 -0
- package/bundled-realms/pretui/internal/ink.gts +25 -0
- package/bundled-realms/pretui/internal/json-tree.gts +526 -0
- package/bundled-realms/pretui/internal/keep-style.gts +168 -0
- package/bundled-realms/pretui/internal/media-assets.gts +10 -0
- package/bundled-realms/pretui/internal/media-library.gts +14 -0
- package/bundled-realms/pretui/internal/media-viewer.gts +322 -0
- package/bundled-realms/pretui/internal/media-wave.gts +270 -0
- package/bundled-realms/pretui/internal/menu.gts +522 -0
- package/bundled-realms/pretui/internal/money-fixtures.gts +6 -0
- package/bundled-realms/pretui/internal/money.gts +477 -0
- package/bundled-realms/pretui/internal/motion-core.gts +54 -0
- package/bundled-realms/pretui/internal/motion-pointer.gts +234 -0
- package/bundled-realms/pretui/internal/overlay-confirm.gts +35 -0
- package/bundled-realms/pretui/internal/overlay.gts +291 -0
- package/bundled-realms/pretui/internal/reading-extras.gts +56 -0
- package/bundled-realms/pretui/internal/reading-format.gts +232 -0
- package/bundled-realms/pretui/internal/reading-listing.gts +232 -0
- package/bundled-realms/pretui/internal/structure-chart.gts +414 -0
- package/bundled-realms/pretui/internal/structure-dashboard.gts +503 -0
- package/bundled-realms/pretui/internal/structure-flow.gts +48 -0
- package/bundled-realms/pretui/internal/structure-layout-fixtures.gts +36 -0
- package/bundled-realms/pretui/internal/structure-layout.gts +99 -0
- package/bundled-realms/pretui/internal/structure-morph.gts +140 -0
- package/bundled-realms/pretui/internal/structure-scenes.gts +25 -0
- package/bundled-realms/pretui/internal/structure-scroll.gts +72 -0
- package/bundled-realms/pretui/internal/structure-shell-fixtures.gts +25 -0
- package/bundled-realms/pretui/internal/surfaces-canvas-fixtures.gts +5 -0
- package/bundled-realms/pretui/internal/surfaces-canvas.gts +283 -0
- package/bundled-realms/pretui/internal/texture.gts +81 -0
- package/bundled-realms/pretui/internal/toggle-controls-fixtures.gts +7 -0
- package/bundled-realms/pretui/internal/toggle-controls.gts +81 -0
- package/bundled-realms/pretui/json-model.ts +1981 -0
- package/bundled-realms/pretui/media-chrome/LICENSE +19 -0
- package/bundled-realms/pretui/media-chrome/README.md +111 -0
- package/bundled-realms/pretui/media-chrome/index.js +1647 -0
- package/bundled-realms/pretui/media-examples.gts +315 -0
- package/bundled-realms/pretui/model-viewer/LICENSE +202 -0
- package/{LICENSE → bundled-realms/pretui/model-viewer/LICENSE-gainmap-js} +1 -1
- package/bundled-realms/pretui/model-viewer/LICENSE-lit +28 -0
- package/bundled-realms/pretui/model-viewer/LICENSE-three +21 -0
- package/bundled-realms/pretui/model-viewer/NOTICE +67 -0
- package/bundled-realms/pretui/model-viewer/README.md +122 -0
- package/bundled-realms/pretui/model-viewer/index.js +6413 -0
- package/bundled-realms/pretui/package.json +51 -0
- package/bundled-realms/pretui/photoswipe/LICENSE +21 -0
- package/bundled-realms/pretui/photoswipe/README.md +91 -0
- package/bundled-realms/pretui/photoswipe/index.js +149 -0
- package/bundled-realms/pretui/photoswipe/style.ts +465 -0
- package/bundled-realms/pretui/plot/LICENSE +13 -0
- package/bundled-realms/pretui/plot/LICENSE.d3 +13 -0
- package/bundled-realms/pretui/plot/LICENSE.interval-tree-1d +22 -0
- package/bundled-realms/pretui/plot/LICENSE.isoformat +13 -0
- package/bundled-realms/pretui/plot/README.md +35 -0
- package/bundled-realms/pretui/plot/index.d.ts +31 -0
- package/bundled-realms/pretui/plot/index.js +449 -0
- package/bundled-realms/pretui/pretui-component.gts +1336 -0
- package/bundled-realms/pretui/pretui-css.gts +380 -0
- package/bundled-realms/pretui/pretui-primitives.gts +162 -0
- package/bundled-realms/pretui/qrcode/LICENSE +34 -0
- package/bundled-realms/pretui/qrcode/README.md +87 -0
- package/bundled-realms/pretui/qrcode/index.js +35 -0
- package/bundled-realms/pretui/realm.json +16 -0
- package/bundled-realms/pretui/registry.ts +40 -0
- package/bundled-realms/pretui/sigpad/LICENSE +21 -0
- package/bundled-realms/pretui/sigpad/README.md +103 -0
- package/bundled-realms/pretui/sigpad/index.js +30 -0
- package/bundled-realms/pretui/structure-dashboard-css.ts +166 -0
- package/bundled-realms/pretui/surfaces/README.md +37 -0
- package/bundled-realms/pretui/surfaces/canvas/LICENSE.d3 +168 -0
- package/bundled-realms/pretui/surfaces/canvas/LICENSE.xyflow +21 -0
- package/bundled-realms/pretui/surfaces/canvas/index.js +25420 -0
- package/bundled-realms/pretui/surfaces/grid/LICENSE.tanstack +50 -0
- package/bundled-realms/pretui/surfaces/grid/index.js +21478 -0
- package/bundled-realms/pretui/surfaces/layout/index.js +9036 -0
- package/bundled-realms/pretui/surfaces-canvas-css.ts +1640 -0
- package/bundled-realms/pretui/surfaces-canvas-layout.ts +607 -0
- package/bundled-realms/pretui/tsconfig.json +54 -0
- package/bundled-realms/pretui/wavesurfer/LICENSE +29 -0
- package/bundled-realms/pretui/wavesurfer/README.md +85 -0
- package/bundled-realms/pretui/wavesurfer/index.js +184 -0
- package/bundled-realms/pretui/zxcvbn/LICENSE +21 -0
- package/bundled-realms/pretui/zxcvbn/README.md +192 -0
- package/bundled-realms/pretui/zxcvbn/index.d.ts +28 -0
- package/bundled-realms/pretui/zxcvbn/index.js +5931 -0
- package/bundled-test-harness/index.html +1 -1
- package/bundled-test-harness/tests/index.html +1 -1
- package/dist/api.js +1 -1
- package/dist/index.js +33 -33
- package/package.json +1 -1
- package/src/lib/test-engine.ts +14 -4
|
@@ -0,0 +1,1981 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* json-model.ts — Pretui's JSON document model.
|
|
3
|
+
*
|
|
4
|
+
* A precision-preserving, diagnostic-emitting JSON parser plus an immutable
|
|
5
|
+
* edit algebra. Pure logic: no DOM, no Glimmer, no timers, no `Date.now()`,
|
|
6
|
+
* no `Math.random()`. Safe to evaluate anywhere in the realm graph.
|
|
7
|
+
*
|
|
8
|
+
* Ported from the interaction model and edge-case handling of JsonTree.js
|
|
9
|
+
* v4.7.1 (MIT, (c) 2025 William Troup / Bunoon). No JsonTree.js code is
|
|
10
|
+
* vendored — see `json-tree.gts` for the porting rationale.
|
|
11
|
+
*
|
|
12
|
+
* BETTER THAN THE INSPIRATION — the four things upstream gets wrong:
|
|
13
|
+
*
|
|
14
|
+
* 1. Upstream's `Convert.jsonStringToObject` falls back to `eval()` when
|
|
15
|
+
* `JSON.parse` throws. That is arbitrary code execution on user input.
|
|
16
|
+
* We hand-roll a tokenizer instead and never evaluate anything.
|
|
17
|
+
* 2. Upstream deletes a key when you clear its value, so `""` is
|
|
18
|
+
* unreachable and a slip of the keyboard is silent data loss. Here,
|
|
19
|
+
* clearing a string yields `""`; removal is only ever an explicit op.
|
|
20
|
+
* 3. Upstream's `Convert.stringToDataTypeValue` coerces every edit back to
|
|
21
|
+
* the ORIGINAL value's type and returns `null` (edit silently dropped)
|
|
22
|
+
* when it cannot. Here, type is an explicit operation and a value that
|
|
23
|
+
* cannot be interpreted stays pending with a diagnostic attached.
|
|
24
|
+
* 4. Upstream round-trips through `JSON.parse`, so a 25-digit integer
|
|
25
|
+
* silently becomes a different number. Here every number keeps its
|
|
26
|
+
* source literal and is re-emitted verbatim.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** A location inside a JSON document. Object steps are keys; array steps are indices. */
|
|
30
|
+
export type JsonPath = ReadonlyArray<string | number>;
|
|
31
|
+
|
|
32
|
+
/** The six JSON types, as a closed union. */
|
|
33
|
+
export type JsonKind =
|
|
34
|
+
| 'null'
|
|
35
|
+
| 'boolean'
|
|
36
|
+
| 'number'
|
|
37
|
+
| 'string'
|
|
38
|
+
| 'array'
|
|
39
|
+
| 'object';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One member of an object. Objects are modelled as an ORDERED LIST of entries
|
|
43
|
+
* rather than a JS object so that key order survives editing and duplicate
|
|
44
|
+
* keys survive parsing (a plain object would silently drop all but the last).
|
|
45
|
+
*/
|
|
46
|
+
export interface JsonEntry {
|
|
47
|
+
readonly key: string;
|
|
48
|
+
readonly node: JsonNode;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type JsonNode =
|
|
52
|
+
| { readonly kind: 'null' }
|
|
53
|
+
| { readonly kind: 'boolean'; readonly value: boolean }
|
|
54
|
+
/**
|
|
55
|
+
* `literal` is the exact source text of the number. It is what gets
|
|
56
|
+
* re-emitted, so integers beyond `Number.MAX_SAFE_INTEGER` and
|
|
57
|
+
* high-precision decimals round-trip byte-for-byte even though `value`
|
|
58
|
+
* (the IEEE-754 reading, kept for display and arithmetic) cannot hold them.
|
|
59
|
+
*/
|
|
60
|
+
| {
|
|
61
|
+
readonly kind: 'number';
|
|
62
|
+
readonly value: number;
|
|
63
|
+
readonly literal: string;
|
|
64
|
+
}
|
|
65
|
+
| { readonly kind: 'string'; readonly value: string }
|
|
66
|
+
| { readonly kind: 'array'; readonly items: ReadonlyArray<JsonNode> }
|
|
67
|
+
| { readonly kind: 'object'; readonly entries: ReadonlyArray<JsonEntry> };
|
|
68
|
+
|
|
69
|
+
export type DiagnosticCode =
|
|
70
|
+
| 'unexpected-end'
|
|
71
|
+
| 'unexpected-token'
|
|
72
|
+
| 'trailing-content'
|
|
73
|
+
| 'bad-number'
|
|
74
|
+
| 'bad-string'
|
|
75
|
+
| 'bad-escape'
|
|
76
|
+
| 'lone-surrogate'
|
|
77
|
+
| 'control-character'
|
|
78
|
+
| 'duplicate-key'
|
|
79
|
+
| 'number-precision'
|
|
80
|
+
| 'empty-document'
|
|
81
|
+
| 'pending-value';
|
|
82
|
+
|
|
83
|
+
export interface JsonDiagnostic {
|
|
84
|
+
readonly severity: 'error' | 'warning';
|
|
85
|
+
readonly code: DiagnosticCode;
|
|
86
|
+
/** Human-readable, already sentence-cased. Safe to render directly. */
|
|
87
|
+
readonly message: string;
|
|
88
|
+
/** Zero-based character offset into the source text, when known. */
|
|
89
|
+
readonly offset: number;
|
|
90
|
+
/** One-based line number, for gutter display. */
|
|
91
|
+
readonly line: number;
|
|
92
|
+
/** One-based column number. */
|
|
93
|
+
readonly column: number;
|
|
94
|
+
/** Where in the document tree the problem sits, when known. */
|
|
95
|
+
readonly path?: JsonPath;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ParseSuccess {
|
|
99
|
+
readonly ok: true;
|
|
100
|
+
readonly root: JsonNode;
|
|
101
|
+
/**
|
|
102
|
+
* Non-fatal findings: duplicate keys, numbers that exceed IEEE-754
|
|
103
|
+
* precision, lone surrogates. The document parsed; the caller decides
|
|
104
|
+
* whether these matter.
|
|
105
|
+
*/
|
|
106
|
+
readonly diagnostics: ReadonlyArray<JsonDiagnostic>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ParseFailure {
|
|
110
|
+
readonly ok: false;
|
|
111
|
+
readonly diagnostics: ReadonlyArray<JsonDiagnostic>;
|
|
112
|
+
/**
|
|
113
|
+
* TRUE when the text ran out mid-value — `{"a": ` or `[1,`. This is the
|
|
114
|
+
* signature of a half-typed document, which is a PENDING state, not a
|
|
115
|
+
* wrong one. UI should stay neutral for `incomplete`, and only show a
|
|
116
|
+
* hard error when this is false.
|
|
117
|
+
*/
|
|
118
|
+
readonly incomplete: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export type ParseResult = ParseSuccess | ParseFailure;
|
|
122
|
+
|
|
123
|
+
/* ------------------------------------------------------------------ *
|
|
124
|
+
* Parser
|
|
125
|
+
* ------------------------------------------------------------------ */
|
|
126
|
+
|
|
127
|
+
const BACKSLASH = '\\';
|
|
128
|
+
const QUOTE = '"';
|
|
129
|
+
|
|
130
|
+
interface Cursor {
|
|
131
|
+
readonly text: string;
|
|
132
|
+
pos: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Convert a character offset into 1-based line/column for gutter display. */
|
|
136
|
+
function locate(
|
|
137
|
+
text: string,
|
|
138
|
+
offset: number,
|
|
139
|
+
): { line: number; column: number } {
|
|
140
|
+
let line = 1;
|
|
141
|
+
let lineStart = 0;
|
|
142
|
+
const limit = Math.min(offset, text.length);
|
|
143
|
+
for (let i = 0; i < limit; i++) {
|
|
144
|
+
if (text.charCodeAt(i) === 10) {
|
|
145
|
+
line++;
|
|
146
|
+
lineStart = i + 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { line, column: limit - lineStart + 1 };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function diagnostic(
|
|
153
|
+
text: string,
|
|
154
|
+
offset: number,
|
|
155
|
+
severity: 'error' | 'warning',
|
|
156
|
+
code: DiagnosticCode,
|
|
157
|
+
message: string,
|
|
158
|
+
path?: JsonPath,
|
|
159
|
+
): JsonDiagnostic {
|
|
160
|
+
const at = locate(text, offset);
|
|
161
|
+
return {
|
|
162
|
+
severity,
|
|
163
|
+
code,
|
|
164
|
+
message,
|
|
165
|
+
offset,
|
|
166
|
+
line: at.line,
|
|
167
|
+
column: at.column,
|
|
168
|
+
...(path ? { path } : {}),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Thrown internally to unwind the recursive-descent parser. Never escapes `parseJson`. */
|
|
173
|
+
class ParseAbort extends Error {
|
|
174
|
+
readonly diag: JsonDiagnostic;
|
|
175
|
+
readonly incomplete: boolean;
|
|
176
|
+
constructor(diag: JsonDiagnostic, incomplete: boolean) {
|
|
177
|
+
super(diag.message);
|
|
178
|
+
this.diag = diag;
|
|
179
|
+
this.incomplete = incomplete;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function isWhitespace(code: number): boolean {
|
|
184
|
+
return code === 32 || code === 9 || code === 10 || code === 13;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function skipWhitespace(cur: Cursor): void {
|
|
188
|
+
while (
|
|
189
|
+
cur.pos < cur.text.length &&
|
|
190
|
+
isWhitespace(cur.text.charCodeAt(cur.pos))
|
|
191
|
+
) {
|
|
192
|
+
cur.pos++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function abortAtEnd(cur: Cursor, what: string): never {
|
|
197
|
+
throw new ParseAbort(
|
|
198
|
+
diagnostic(
|
|
199
|
+
cur.text,
|
|
200
|
+
cur.text.length,
|
|
201
|
+
'error',
|
|
202
|
+
'unexpected-end',
|
|
203
|
+
`The document ends before ${what} is complete.`,
|
|
204
|
+
),
|
|
205
|
+
true,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Normalise a JSON number literal to `{negative, significand, exponent}` with
|
|
211
|
+
* no leading or trailing zeros, so two literals can be compared for exact
|
|
212
|
+
* mathematical equality. Used to detect precision loss without any float
|
|
213
|
+
* arithmetic.
|
|
214
|
+
*/
|
|
215
|
+
function canonicalNumber(literal: string): {
|
|
216
|
+
negative: boolean;
|
|
217
|
+
significand: string;
|
|
218
|
+
exponent: number;
|
|
219
|
+
} {
|
|
220
|
+
let rest = literal;
|
|
221
|
+
let negative = false;
|
|
222
|
+
if (rest.startsWith('-')) {
|
|
223
|
+
negative = true;
|
|
224
|
+
rest = rest.slice(1);
|
|
225
|
+
} else if (rest.startsWith('+')) {
|
|
226
|
+
rest = rest.slice(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let exponent = 0;
|
|
230
|
+
const eIndex = rest.search(/[eE]/);
|
|
231
|
+
if (eIndex >= 0) {
|
|
232
|
+
exponent = parseInt(rest.slice(eIndex + 1), 10) || 0;
|
|
233
|
+
rest = rest.slice(0, eIndex);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const dotIndex = rest.indexOf('.');
|
|
237
|
+
let digits = rest;
|
|
238
|
+
if (dotIndex >= 0) {
|
|
239
|
+
const fraction = rest.slice(dotIndex + 1);
|
|
240
|
+
digits = rest.slice(0, dotIndex) + fraction;
|
|
241
|
+
exponent -= fraction.length;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Strip leading zeros.
|
|
245
|
+
let start = 0;
|
|
246
|
+
while (start < digits.length - 1 && digits.charCodeAt(start) === 48) {
|
|
247
|
+
start++;
|
|
248
|
+
}
|
|
249
|
+
digits = digits.slice(start);
|
|
250
|
+
|
|
251
|
+
// Strip trailing zeros, folding each into the exponent.
|
|
252
|
+
let end = digits.length;
|
|
253
|
+
while (end > 0 && digits.charCodeAt(end - 1) === 48) {
|
|
254
|
+
end--;
|
|
255
|
+
exponent++;
|
|
256
|
+
}
|
|
257
|
+
digits = digits.slice(0, end);
|
|
258
|
+
|
|
259
|
+
if (digits.length === 0) {
|
|
260
|
+
// The value is exactly zero; sign and exponent carry no information.
|
|
261
|
+
return { negative: false, significand: '', exponent: 0 };
|
|
262
|
+
}
|
|
263
|
+
return { negative, significand: digits, exponent };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* TRUE when `literal` survives a round-trip through an IEEE-754 double.
|
|
268
|
+
* `9007199254740993` and `0.1000000000000000055511151231257827` do not.
|
|
269
|
+
*/
|
|
270
|
+
export function numberIsExact(literal: string): boolean {
|
|
271
|
+
const value = Number(literal);
|
|
272
|
+
if (!Number.isFinite(value)) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const source = canonicalNumber(literal);
|
|
276
|
+
const roundTrip = canonicalNumber(String(value));
|
|
277
|
+
return (
|
|
278
|
+
source.negative === roundTrip.negative &&
|
|
279
|
+
source.significand === roundTrip.significand &&
|
|
280
|
+
source.exponent === roundTrip.exponent
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const NUMBER_PATTERN = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?/;
|
|
285
|
+
|
|
286
|
+
function parseNumberToken(
|
|
287
|
+
cur: Cursor,
|
|
288
|
+
warnings: JsonDiagnostic[],
|
|
289
|
+
path: JsonPath,
|
|
290
|
+
): JsonNode {
|
|
291
|
+
const start = cur.pos;
|
|
292
|
+
const match = NUMBER_PATTERN.exec(cur.text.slice(start));
|
|
293
|
+
if (!match || match[0].length === 0) {
|
|
294
|
+
throw new ParseAbort(
|
|
295
|
+
diagnostic(
|
|
296
|
+
cur.text,
|
|
297
|
+
start,
|
|
298
|
+
'error',
|
|
299
|
+
'bad-number',
|
|
300
|
+
'This is not a valid JSON number. JSON has no leading `+`, no leading zeros, no `.5`, and no `Infinity` or `NaN`.',
|
|
301
|
+
path,
|
|
302
|
+
),
|
|
303
|
+
false,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const literal = match[0];
|
|
307
|
+
cur.pos = start + literal.length;
|
|
308
|
+
|
|
309
|
+
// A number immediately followed by more number-ish characters is a typo,
|
|
310
|
+
// not two tokens: `01`, `1.2.3`, `1e`, `0x10`.
|
|
311
|
+
const nextCode =
|
|
312
|
+
cur.pos < cur.text.length ? cur.text.charCodeAt(cur.pos) : -1;
|
|
313
|
+
const nextChar = nextCode >= 0 ? String.fromCharCode(nextCode) : '';
|
|
314
|
+
if (nextChar !== '' && /[0-9a-zA-Z._+-]/.test(nextChar)) {
|
|
315
|
+
throw new ParseAbort(
|
|
316
|
+
diagnostic(
|
|
317
|
+
cur.text,
|
|
318
|
+
cur.pos,
|
|
319
|
+
'error',
|
|
320
|
+
'bad-number',
|
|
321
|
+
'This is not a valid JSON number. JSON has no leading `+`, no leading zeros, no `.5`, and no `Infinity` or `NaN`.',
|
|
322
|
+
path,
|
|
323
|
+
),
|
|
324
|
+
cur.pos >= cur.text.length,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const value = Number(literal);
|
|
329
|
+
if (!numberIsExact(literal)) {
|
|
330
|
+
warnings.push(
|
|
331
|
+
diagnostic(
|
|
332
|
+
cur.text,
|
|
333
|
+
start,
|
|
334
|
+
'warning',
|
|
335
|
+
'number-precision',
|
|
336
|
+
`The number ${literal} is larger or more precise than JavaScript numbers can hold. Its exact text is preserved, but arithmetic on it would lose precision.`,
|
|
337
|
+
path,
|
|
338
|
+
),
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return { kind: 'number', value, literal };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function parseStringToken(
|
|
345
|
+
cur: Cursor,
|
|
346
|
+
warnings: JsonDiagnostic[],
|
|
347
|
+
path: JsonPath,
|
|
348
|
+
): string {
|
|
349
|
+
cur.pos++; // consume the opening quote
|
|
350
|
+
let out = '';
|
|
351
|
+
|
|
352
|
+
for (;;) {
|
|
353
|
+
if (cur.pos >= cur.text.length) {
|
|
354
|
+
throw new ParseAbort(
|
|
355
|
+
diagnostic(
|
|
356
|
+
cur.text,
|
|
357
|
+
cur.text.length,
|
|
358
|
+
'error',
|
|
359
|
+
'unexpected-end',
|
|
360
|
+
'The document ends inside a string — the closing quote is missing.',
|
|
361
|
+
path,
|
|
362
|
+
),
|
|
363
|
+
true,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const code = cur.text.charCodeAt(cur.pos);
|
|
368
|
+
|
|
369
|
+
if (code === 34) {
|
|
370
|
+
cur.pos++;
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (code === 92) {
|
|
375
|
+
cur.pos++;
|
|
376
|
+
if (cur.pos >= cur.text.length) {
|
|
377
|
+
abortAtEnd(cur, 'an escape sequence');
|
|
378
|
+
}
|
|
379
|
+
const esc = cur.text[cur.pos];
|
|
380
|
+
cur.pos++;
|
|
381
|
+
if (esc === QUOTE) {
|
|
382
|
+
out += QUOTE;
|
|
383
|
+
} else if (esc === BACKSLASH) {
|
|
384
|
+
out += BACKSLASH;
|
|
385
|
+
} else if (esc === '/') {
|
|
386
|
+
out += '/';
|
|
387
|
+
} else if (esc === 'b') {
|
|
388
|
+
out += '\b';
|
|
389
|
+
} else if (esc === 'f') {
|
|
390
|
+
out += '\f';
|
|
391
|
+
} else if (esc === 'n') {
|
|
392
|
+
out += '\n';
|
|
393
|
+
} else if (esc === 'r') {
|
|
394
|
+
out += '\r';
|
|
395
|
+
} else if (esc === 't') {
|
|
396
|
+
out += '\t';
|
|
397
|
+
} else if (esc === 'u') {
|
|
398
|
+
const hex = cur.text.slice(cur.pos, cur.pos + 4);
|
|
399
|
+
if (hex.length < 4) {
|
|
400
|
+
abortAtEnd(cur, 'a `\\u` escape');
|
|
401
|
+
}
|
|
402
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
403
|
+
throw new ParseAbort(
|
|
404
|
+
diagnostic(
|
|
405
|
+
cur.text,
|
|
406
|
+
cur.pos,
|
|
407
|
+
'error',
|
|
408
|
+
'bad-escape',
|
|
409
|
+
'A `\\u` escape needs exactly four hexadecimal digits.',
|
|
410
|
+
path,
|
|
411
|
+
),
|
|
412
|
+
false,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
const unit = parseInt(hex, 16);
|
|
416
|
+
cur.pos += 4;
|
|
417
|
+
out += String.fromCharCode(unit);
|
|
418
|
+
if (unit >= 0xd800 && unit <= 0xdfff) {
|
|
419
|
+
// Only warn once the pair is resolvable; a high surrogate followed
|
|
420
|
+
// by its low partner is perfectly legal.
|
|
421
|
+
const previous = out.charCodeAt(out.length - 2);
|
|
422
|
+
const isCompletedPair =
|
|
423
|
+
unit >= 0xdc00 && previous >= 0xd800 && previous <= 0xdbff;
|
|
424
|
+
if (!isCompletedPair && unit >= 0xdc00) {
|
|
425
|
+
warnings.push(
|
|
426
|
+
diagnostic(
|
|
427
|
+
cur.text,
|
|
428
|
+
cur.pos - 6,
|
|
429
|
+
'warning',
|
|
430
|
+
'lone-surrogate',
|
|
431
|
+
'This string contains an unpaired surrogate escape. It is preserved exactly, but it is not valid Unicode text.',
|
|
432
|
+
path,
|
|
433
|
+
),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
throw new ParseAbort(
|
|
439
|
+
diagnostic(
|
|
440
|
+
cur.text,
|
|
441
|
+
cur.pos - 1,
|
|
442
|
+
'error',
|
|
443
|
+
'bad-escape',
|
|
444
|
+
`\\${esc} is not a JSON escape sequence. JSON allows \\" \\\\ \\/ \\b \\f \\n \\r \\t and \\uXXXX.`,
|
|
445
|
+
path,
|
|
446
|
+
),
|
|
447
|
+
false,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (code < 0x20) {
|
|
454
|
+
throw new ParseAbort(
|
|
455
|
+
diagnostic(
|
|
456
|
+
cur.text,
|
|
457
|
+
cur.pos,
|
|
458
|
+
'error',
|
|
459
|
+
'control-character',
|
|
460
|
+
'A raw control character is not allowed inside a JSON string. Write it as an escape such as `\\n`.',
|
|
461
|
+
path,
|
|
462
|
+
),
|
|
463
|
+
false,
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
out += cur.text[cur.pos];
|
|
468
|
+
cur.pos++;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function parseValue(
|
|
473
|
+
cur: Cursor,
|
|
474
|
+
warnings: JsonDiagnostic[],
|
|
475
|
+
path: JsonPath,
|
|
476
|
+
): JsonNode {
|
|
477
|
+
skipWhitespace(cur);
|
|
478
|
+
if (cur.pos >= cur.text.length) {
|
|
479
|
+
abortAtEnd(cur, 'a value');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const char = cur.text[cur.pos];
|
|
483
|
+
|
|
484
|
+
if (char === QUOTE) {
|
|
485
|
+
return { kind: 'string', value: parseStringToken(cur, warnings, path) };
|
|
486
|
+
}
|
|
487
|
+
if (char === '{') {
|
|
488
|
+
return parseObject(cur, warnings, path);
|
|
489
|
+
}
|
|
490
|
+
if (char === '[') {
|
|
491
|
+
return parseArray(cur, warnings, path);
|
|
492
|
+
}
|
|
493
|
+
if (cur.text.startsWith('true', cur.pos)) {
|
|
494
|
+
cur.pos += 4;
|
|
495
|
+
return { kind: 'boolean', value: true };
|
|
496
|
+
}
|
|
497
|
+
if (cur.text.startsWith('false', cur.pos)) {
|
|
498
|
+
cur.pos += 5;
|
|
499
|
+
return { kind: 'boolean', value: false };
|
|
500
|
+
}
|
|
501
|
+
if (cur.text.startsWith('null', cur.pos)) {
|
|
502
|
+
cur.pos += 4;
|
|
503
|
+
return { kind: 'null' };
|
|
504
|
+
}
|
|
505
|
+
if (char === '-' || (char >= '0' && char <= '9')) {
|
|
506
|
+
return parseNumberToken(cur, warnings, path);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// A bare prefix of a keyword at end-of-input is a half-typed document.
|
|
510
|
+
const tail = cur.text.slice(cur.pos);
|
|
511
|
+
const isKeywordPrefix =
|
|
512
|
+
'true'.startsWith(tail) ||
|
|
513
|
+
'false'.startsWith(tail) ||
|
|
514
|
+
'null'.startsWith(tail);
|
|
515
|
+
if (isKeywordPrefix && tail.length > 0) {
|
|
516
|
+
abortAtEnd(cur, 'a value');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
throw new ParseAbort(
|
|
520
|
+
diagnostic(
|
|
521
|
+
cur.text,
|
|
522
|
+
cur.pos,
|
|
523
|
+
'error',
|
|
524
|
+
'unexpected-token',
|
|
525
|
+
`Expected a value here, found \`${char}\`. A JSON value is an object, array, string, number, \`true\`, \`false\`, or \`null\`.`,
|
|
526
|
+
path,
|
|
527
|
+
),
|
|
528
|
+
false,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function parseArray(
|
|
533
|
+
cur: Cursor,
|
|
534
|
+
warnings: JsonDiagnostic[],
|
|
535
|
+
path: JsonPath,
|
|
536
|
+
): JsonNode {
|
|
537
|
+
cur.pos++; // consume `[`
|
|
538
|
+
const items: JsonNode[] = [];
|
|
539
|
+
skipWhitespace(cur);
|
|
540
|
+
|
|
541
|
+
if (cur.pos >= cur.text.length) {
|
|
542
|
+
abortAtEnd(cur, 'an array');
|
|
543
|
+
}
|
|
544
|
+
if (cur.text[cur.pos] === ']') {
|
|
545
|
+
cur.pos++;
|
|
546
|
+
return { kind: 'array', items };
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
for (;;) {
|
|
550
|
+
items.push(parseValue(cur, warnings, [...path, items.length]));
|
|
551
|
+
skipWhitespace(cur);
|
|
552
|
+
if (cur.pos >= cur.text.length) {
|
|
553
|
+
abortAtEnd(cur, 'an array');
|
|
554
|
+
}
|
|
555
|
+
const char = cur.text[cur.pos];
|
|
556
|
+
if (char === ',') {
|
|
557
|
+
cur.pos++;
|
|
558
|
+
skipWhitespace(cur);
|
|
559
|
+
if (cur.pos >= cur.text.length) {
|
|
560
|
+
abortAtEnd(cur, 'an array');
|
|
561
|
+
}
|
|
562
|
+
if (cur.text[cur.pos] === ']') {
|
|
563
|
+
throw new ParseAbort(
|
|
564
|
+
diagnostic(
|
|
565
|
+
cur.text,
|
|
566
|
+
cur.pos,
|
|
567
|
+
'error',
|
|
568
|
+
'unexpected-token',
|
|
569
|
+
'JSON does not allow a trailing comma before `]`.',
|
|
570
|
+
path,
|
|
571
|
+
),
|
|
572
|
+
false,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
if (char === ']') {
|
|
578
|
+
cur.pos++;
|
|
579
|
+
return { kind: 'array', items };
|
|
580
|
+
}
|
|
581
|
+
throw new ParseAbort(
|
|
582
|
+
diagnostic(
|
|
583
|
+
cur.text,
|
|
584
|
+
cur.pos,
|
|
585
|
+
'error',
|
|
586
|
+
'unexpected-token',
|
|
587
|
+
`Expected \`,\` or \`]\` here, found \`${char}\`.`,
|
|
588
|
+
path,
|
|
589
|
+
),
|
|
590
|
+
false,
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function parseObject(
|
|
596
|
+
cur: Cursor,
|
|
597
|
+
warnings: JsonDiagnostic[],
|
|
598
|
+
path: JsonPath,
|
|
599
|
+
): JsonNode {
|
|
600
|
+
cur.pos++; // consume `{`
|
|
601
|
+
const entries: JsonEntry[] = [];
|
|
602
|
+
const seen = new Set<string>();
|
|
603
|
+
skipWhitespace(cur);
|
|
604
|
+
|
|
605
|
+
if (cur.pos >= cur.text.length) {
|
|
606
|
+
abortAtEnd(cur, 'an object');
|
|
607
|
+
}
|
|
608
|
+
if (cur.text[cur.pos] === '}') {
|
|
609
|
+
cur.pos++;
|
|
610
|
+
return { kind: 'object', entries };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
for (;;) {
|
|
614
|
+
skipWhitespace(cur);
|
|
615
|
+
if (cur.pos >= cur.text.length) {
|
|
616
|
+
abortAtEnd(cur, 'an object');
|
|
617
|
+
}
|
|
618
|
+
if (cur.text[cur.pos] !== QUOTE) {
|
|
619
|
+
throw new ParseAbort(
|
|
620
|
+
diagnostic(
|
|
621
|
+
cur.text,
|
|
622
|
+
cur.pos,
|
|
623
|
+
'error',
|
|
624
|
+
'unexpected-token',
|
|
625
|
+
`Expected a quoted property name here, found \`${cur.text[cur.pos]}\`.`,
|
|
626
|
+
path,
|
|
627
|
+
),
|
|
628
|
+
false,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
const keyOffset = cur.pos;
|
|
632
|
+
const key = parseStringToken(cur, warnings, path);
|
|
633
|
+
|
|
634
|
+
if (seen.has(key)) {
|
|
635
|
+
// JSON.parse keeps the last and drops the rest without a word. We keep
|
|
636
|
+
// BOTH entries and flag it, because dropping a key is data loss.
|
|
637
|
+
warnings.push(
|
|
638
|
+
diagnostic(
|
|
639
|
+
cur.text,
|
|
640
|
+
keyOffset,
|
|
641
|
+
'warning',
|
|
642
|
+
'duplicate-key',
|
|
643
|
+
`The property name "${key}" appears more than once in this object. Both entries are preserved here, but most JSON readers keep only the last.`,
|
|
644
|
+
[...path, key],
|
|
645
|
+
),
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
seen.add(key);
|
|
649
|
+
|
|
650
|
+
skipWhitespace(cur);
|
|
651
|
+
if (cur.pos >= cur.text.length) {
|
|
652
|
+
abortAtEnd(cur, 'an object');
|
|
653
|
+
}
|
|
654
|
+
if (cur.text[cur.pos] !== ':') {
|
|
655
|
+
throw new ParseAbort(
|
|
656
|
+
diagnostic(
|
|
657
|
+
cur.text,
|
|
658
|
+
cur.pos,
|
|
659
|
+
'error',
|
|
660
|
+
'unexpected-token',
|
|
661
|
+
`Expected \`:\` after the property name, found \`${cur.text[cur.pos]}\`.`,
|
|
662
|
+
[...path, key],
|
|
663
|
+
),
|
|
664
|
+
false,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
cur.pos++;
|
|
668
|
+
|
|
669
|
+
const node = parseValue(cur, warnings, [...path, key]);
|
|
670
|
+
entries.push({ key, node });
|
|
671
|
+
|
|
672
|
+
skipWhitespace(cur);
|
|
673
|
+
if (cur.pos >= cur.text.length) {
|
|
674
|
+
abortAtEnd(cur, 'an object');
|
|
675
|
+
}
|
|
676
|
+
const char = cur.text[cur.pos];
|
|
677
|
+
if (char === ',') {
|
|
678
|
+
cur.pos++;
|
|
679
|
+
skipWhitespace(cur);
|
|
680
|
+
if (cur.pos >= cur.text.length) {
|
|
681
|
+
abortAtEnd(cur, 'an object');
|
|
682
|
+
}
|
|
683
|
+
if (cur.text[cur.pos] === '}') {
|
|
684
|
+
throw new ParseAbort(
|
|
685
|
+
diagnostic(
|
|
686
|
+
cur.text,
|
|
687
|
+
cur.pos,
|
|
688
|
+
'error',
|
|
689
|
+
'unexpected-token',
|
|
690
|
+
'JSON does not allow a trailing comma before `}`.',
|
|
691
|
+
path,
|
|
692
|
+
),
|
|
693
|
+
false,
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
continue;
|
|
697
|
+
}
|
|
698
|
+
if (char === '}') {
|
|
699
|
+
cur.pos++;
|
|
700
|
+
return { kind: 'object', entries };
|
|
701
|
+
}
|
|
702
|
+
throw new ParseAbort(
|
|
703
|
+
diagnostic(
|
|
704
|
+
cur.text,
|
|
705
|
+
cur.pos,
|
|
706
|
+
'error',
|
|
707
|
+
'unexpected-token',
|
|
708
|
+
`Expected \`,\` or \`}\` here, found \`${char}\`.`,
|
|
709
|
+
path,
|
|
710
|
+
),
|
|
711
|
+
false,
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Parse JSON text into a `JsonNode`, preserving number literals and duplicate
|
|
718
|
+
* keys, and reporting precise diagnostics.
|
|
719
|
+
*
|
|
720
|
+
* Never throws and never calls `eval`. An empty or whitespace-only document
|
|
721
|
+
* fails with `incomplete: true`, because "nothing typed yet" is a pending
|
|
722
|
+
* state rather than a mistake.
|
|
723
|
+
*/
|
|
724
|
+
export function parseJson(text: string): ParseResult {
|
|
725
|
+
const warnings: JsonDiagnostic[] = [];
|
|
726
|
+
const cur: Cursor = { text, pos: 0 };
|
|
727
|
+
|
|
728
|
+
skipWhitespace(cur);
|
|
729
|
+
if (cur.pos >= text.length) {
|
|
730
|
+
return {
|
|
731
|
+
ok: false,
|
|
732
|
+
incomplete: true,
|
|
733
|
+
diagnostics: [
|
|
734
|
+
diagnostic(
|
|
735
|
+
text,
|
|
736
|
+
0,
|
|
737
|
+
'error',
|
|
738
|
+
'empty-document',
|
|
739
|
+
'Nothing has been entered yet.',
|
|
740
|
+
),
|
|
741
|
+
],
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
try {
|
|
746
|
+
const root = parseValue(cur, warnings, []);
|
|
747
|
+
skipWhitespace(cur);
|
|
748
|
+
if (cur.pos < text.length) {
|
|
749
|
+
return {
|
|
750
|
+
ok: false,
|
|
751
|
+
incomplete: false,
|
|
752
|
+
diagnostics: [
|
|
753
|
+
...warnings,
|
|
754
|
+
diagnostic(
|
|
755
|
+
text,
|
|
756
|
+
cur.pos,
|
|
757
|
+
'error',
|
|
758
|
+
'trailing-content',
|
|
759
|
+
'There is extra content after the end of the JSON document.',
|
|
760
|
+
),
|
|
761
|
+
],
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
return { ok: true, root, diagnostics: warnings };
|
|
765
|
+
} catch (error) {
|
|
766
|
+
if (error instanceof ParseAbort) {
|
|
767
|
+
return {
|
|
768
|
+
ok: false,
|
|
769
|
+
incomplete: error.incomplete,
|
|
770
|
+
diagnostics: [...warnings, error.diag],
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/* ------------------------------------------------------------------ *
|
|
778
|
+
* Serialisation
|
|
779
|
+
* ------------------------------------------------------------------ */
|
|
780
|
+
|
|
781
|
+
const ESCAPE_MAP: Record<string, string> = {
|
|
782
|
+
[QUOTE]: BACKSLASH + QUOTE,
|
|
783
|
+
[BACKSLASH]: BACKSLASH + BACKSLASH,
|
|
784
|
+
'\b': BACKSLASH + 'b',
|
|
785
|
+
'\f': BACKSLASH + 'f',
|
|
786
|
+
'\n': BACKSLASH + 'n',
|
|
787
|
+
'\r': BACKSLASH + 'r',
|
|
788
|
+
'\t': BACKSLASH + 't',
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
/** Quote and escape a string as a JSON string literal, including lone surrogates. */
|
|
792
|
+
export function quoteJsonString(value: string): string {
|
|
793
|
+
let out = QUOTE;
|
|
794
|
+
for (let i = 0; i < value.length; i++) {
|
|
795
|
+
const char = value[i]!;
|
|
796
|
+
const code = value.charCodeAt(i);
|
|
797
|
+
const mapped = ESCAPE_MAP[char];
|
|
798
|
+
if (mapped !== undefined) {
|
|
799
|
+
out += mapped;
|
|
800
|
+
} else if (code < 0x20 || (code >= 0xd800 && code <= 0xdfff)) {
|
|
801
|
+
// Control characters must be escaped; surrogates are escaped so that a
|
|
802
|
+
// lone one survives transport intact rather than becoming U+FFFD.
|
|
803
|
+
const isPaired =
|
|
804
|
+
code <= 0xdbff &&
|
|
805
|
+
i + 1 < value.length &&
|
|
806
|
+
value.charCodeAt(i + 1) >= 0xdc00 &&
|
|
807
|
+
value.charCodeAt(i + 1) <= 0xdfff;
|
|
808
|
+
if (isPaired) {
|
|
809
|
+
out += char;
|
|
810
|
+
} else {
|
|
811
|
+
out += BACKSLASH + 'u' + code.toString(16).padStart(4, '0');
|
|
812
|
+
}
|
|
813
|
+
} else {
|
|
814
|
+
out += char;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return out + QUOTE;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Render a node back to JSON text. Number literals are emitted verbatim, so
|
|
822
|
+
* `stringifyJson(parseJson(text).root)` is text-identical to `text` up to
|
|
823
|
+
* whitespace for every document this parser accepts.
|
|
824
|
+
*
|
|
825
|
+
* @param indent spaces per level; `0` produces a single compact line.
|
|
826
|
+
*/
|
|
827
|
+
export function stringifyJson(node: JsonNode, indent = 2): string {
|
|
828
|
+
const pad = indent > 0 ? ' '.repeat(indent) : '';
|
|
829
|
+
const newline = indent > 0 ? '\n' : '';
|
|
830
|
+
const colon = indent > 0 ? ': ' : ':';
|
|
831
|
+
|
|
832
|
+
const render = (current: JsonNode, depth: number): string => {
|
|
833
|
+
const here = indent > 0 ? pad.repeat(depth) : '';
|
|
834
|
+
const inner = indent > 0 ? pad.repeat(depth + 1) : '';
|
|
835
|
+
|
|
836
|
+
switch (current.kind) {
|
|
837
|
+
case 'null':
|
|
838
|
+
return 'null';
|
|
839
|
+
case 'boolean':
|
|
840
|
+
return current.value ? 'true' : 'false';
|
|
841
|
+
case 'number':
|
|
842
|
+
return current.literal;
|
|
843
|
+
case 'string':
|
|
844
|
+
return quoteJsonString(current.value);
|
|
845
|
+
case 'array': {
|
|
846
|
+
if (current.items.length === 0) {
|
|
847
|
+
return '[]';
|
|
848
|
+
}
|
|
849
|
+
const parts = current.items.map(
|
|
850
|
+
(item) => inner + render(item, depth + 1),
|
|
851
|
+
);
|
|
852
|
+
return '[' + newline + parts.join(',' + newline) + newline + here + ']';
|
|
853
|
+
}
|
|
854
|
+
case 'object': {
|
|
855
|
+
if (current.entries.length === 0) {
|
|
856
|
+
return '{}';
|
|
857
|
+
}
|
|
858
|
+
const parts = current.entries.map(
|
|
859
|
+
(entry) =>
|
|
860
|
+
inner +
|
|
861
|
+
quoteJsonString(entry.key) +
|
|
862
|
+
colon +
|
|
863
|
+
render(entry.node, depth + 1),
|
|
864
|
+
);
|
|
865
|
+
return '{' + newline + parts.join(',' + newline) + newline + here + '}';
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
|
|
870
|
+
return render(node, 0);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Convert a node to a plain JavaScript value.
|
|
875
|
+
*
|
|
876
|
+
* LOSSY BY DESIGN, and it says so: numbers beyond IEEE-754 range collapse to
|
|
877
|
+
* their nearest double, and duplicate keys collapse to the last occurrence —
|
|
878
|
+
* exactly what `JSON.parse` would have done. Use `stringifyJson` when the
|
|
879
|
+
* document must survive intact; use this when a caller wants ordinary data.
|
|
880
|
+
*/
|
|
881
|
+
export function toPlainValue(node: JsonNode): unknown {
|
|
882
|
+
switch (node.kind) {
|
|
883
|
+
case 'null':
|
|
884
|
+
return null;
|
|
885
|
+
case 'boolean':
|
|
886
|
+
return node.value;
|
|
887
|
+
case 'number':
|
|
888
|
+
return node.value;
|
|
889
|
+
case 'string':
|
|
890
|
+
return node.value;
|
|
891
|
+
case 'array':
|
|
892
|
+
return node.items.map(toPlainValue);
|
|
893
|
+
case 'object': {
|
|
894
|
+
const out: Record<string, unknown> = {};
|
|
895
|
+
for (const entry of node.entries) {
|
|
896
|
+
out[entry.key] = toPlainValue(entry.node);
|
|
897
|
+
}
|
|
898
|
+
return out;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** Build a node from a plain JavaScript value. Unsupported types become `null`. */
|
|
904
|
+
export function fromPlainValue(value: unknown): JsonNode {
|
|
905
|
+
if (value === null || value === undefined) {
|
|
906
|
+
return { kind: 'null' };
|
|
907
|
+
}
|
|
908
|
+
if (typeof value === 'boolean') {
|
|
909
|
+
return { kind: 'boolean', value };
|
|
910
|
+
}
|
|
911
|
+
if (typeof value === 'number') {
|
|
912
|
+
if (!Number.isFinite(value)) {
|
|
913
|
+
return { kind: 'null' };
|
|
914
|
+
}
|
|
915
|
+
return { kind: 'number', value, literal: String(value) };
|
|
916
|
+
}
|
|
917
|
+
if (typeof value === 'bigint') {
|
|
918
|
+
return { kind: 'number', value: Number(value), literal: value.toString() };
|
|
919
|
+
}
|
|
920
|
+
if (typeof value === 'string') {
|
|
921
|
+
return { kind: 'string', value };
|
|
922
|
+
}
|
|
923
|
+
if (Array.isArray(value)) {
|
|
924
|
+
return { kind: 'array', items: value.map(fromPlainValue) };
|
|
925
|
+
}
|
|
926
|
+
if (typeof value === 'object') {
|
|
927
|
+
const entries: JsonEntry[] = Object.keys(value as object).map((key) => ({
|
|
928
|
+
key,
|
|
929
|
+
node: fromPlainValue((value as Record<string, unknown>)[key]),
|
|
930
|
+
}));
|
|
931
|
+
return { kind: 'object', entries };
|
|
932
|
+
}
|
|
933
|
+
return { kind: 'null' };
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/* ------------------------------------------------------------------ *
|
|
937
|
+
* Navigation
|
|
938
|
+
* ------------------------------------------------------------------ */
|
|
939
|
+
|
|
940
|
+
/** Read the node at `path`, or `undefined` if the path does not resolve. */
|
|
941
|
+
export function nodeAt(root: JsonNode, path: JsonPath): JsonNode | undefined {
|
|
942
|
+
let current: JsonNode | undefined = root;
|
|
943
|
+
for (const step of path) {
|
|
944
|
+
const here: JsonNode | undefined = current;
|
|
945
|
+
if (here === undefined) {
|
|
946
|
+
return undefined;
|
|
947
|
+
}
|
|
948
|
+
if (here.kind === 'array' && typeof step === 'number') {
|
|
949
|
+
current = here.items[step];
|
|
950
|
+
} else if (here.kind === 'object' && typeof step === 'string') {
|
|
951
|
+
const hit: JsonEntry | undefined = here.entries.find(
|
|
952
|
+
(entry: JsonEntry) => entry.key === step,
|
|
953
|
+
);
|
|
954
|
+
current = hit === undefined ? undefined : hit.node;
|
|
955
|
+
} else {
|
|
956
|
+
return undefined;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return current;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Render a path as a JavaScript accessor expression — `$.users[0].name`.
|
|
964
|
+
* This is the "copy path" payload.
|
|
965
|
+
*/
|
|
966
|
+
export function formatPath(path: JsonPath): string {
|
|
967
|
+
let out = '$';
|
|
968
|
+
for (const step of path) {
|
|
969
|
+
if (typeof step === 'number') {
|
|
970
|
+
out += '[' + String(step) + ']';
|
|
971
|
+
} else if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(step)) {
|
|
972
|
+
out += '.' + step;
|
|
973
|
+
} else {
|
|
974
|
+
out += '[' + quoteJsonString(step) + ']';
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return out;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
/**
|
|
981
|
+
* A stable, collision-free string id for a path. Used for DOM ids, `{{#each}}`
|
|
982
|
+
* keys and the expansion set.
|
|
983
|
+
*
|
|
984
|
+
* The escaping is load-bearing: without it `['a', 'b']` and `['a.b']` both
|
|
985
|
+
* render as `.a.b`, so two unrelated nodes would share one expansion state.
|
|
986
|
+
*/
|
|
987
|
+
export function pathKey(path: JsonPath): string {
|
|
988
|
+
return path
|
|
989
|
+
.map((step) =>
|
|
990
|
+
typeof step === 'number'
|
|
991
|
+
? '#' + String(step)
|
|
992
|
+
: '.' + step.replace(/[\\.#]/g, (char) => '\\' + char),
|
|
993
|
+
)
|
|
994
|
+
.join('');
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/** The number of direct children a node has; `0` for scalars. */
|
|
998
|
+
export function childCount(node: JsonNode): number {
|
|
999
|
+
if (node.kind === 'array') {
|
|
1000
|
+
return node.items.length;
|
|
1001
|
+
}
|
|
1002
|
+
if (node.kind === 'object') {
|
|
1003
|
+
return node.entries.length;
|
|
1004
|
+
}
|
|
1005
|
+
return 0;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/** TRUE for arrays and objects — the nodes that can be expanded. */
|
|
1009
|
+
export function isContainer(node: JsonNode): boolean {
|
|
1010
|
+
return node.kind === 'array' || node.kind === 'object';
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/* ------------------------------------------------------------------ *
|
|
1014
|
+
* Edit algebra — every operation is pure and returns a NEW root
|
|
1015
|
+
* ------------------------------------------------------------------ */
|
|
1016
|
+
|
|
1017
|
+
export interface EditResult {
|
|
1018
|
+
readonly root: JsonNode;
|
|
1019
|
+
/** Whether anything actually changed. A no-op must not push an undo frame. */
|
|
1020
|
+
readonly changed: boolean;
|
|
1021
|
+
/** Present when the operation was rejected or degraded. Never silent. */
|
|
1022
|
+
readonly diagnostics: ReadonlyArray<JsonDiagnostic>;
|
|
1023
|
+
/**
|
|
1024
|
+
* A short, past-tense sentence describing the change, for the polite live
|
|
1025
|
+
* region — "Added property colour" / "Removed item 3 of 5".
|
|
1026
|
+
*/
|
|
1027
|
+
readonly announcement: string;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
function ok(root: JsonNode, announcement: string): EditResult {
|
|
1031
|
+
return { root, changed: true, diagnostics: [], announcement };
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function noop(root: JsonNode, announcement: string): EditResult {
|
|
1035
|
+
return { root, changed: false, diagnostics: [], announcement };
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
function rejected(
|
|
1039
|
+
root: JsonNode,
|
|
1040
|
+
code: DiagnosticCode,
|
|
1041
|
+
message: string,
|
|
1042
|
+
path: JsonPath,
|
|
1043
|
+
): EditResult {
|
|
1044
|
+
return {
|
|
1045
|
+
root,
|
|
1046
|
+
changed: false,
|
|
1047
|
+
diagnostics: [
|
|
1048
|
+
{ severity: 'error', code, message, offset: 0, line: 1, column: 1, path },
|
|
1049
|
+
],
|
|
1050
|
+
announcement: message,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* Rebuild the spine from `root` down to `path`, replacing the node there with
|
|
1056
|
+
* `make(existing)`. Structural sharing keeps untouched subtrees identical by
|
|
1057
|
+
* reference, which is what makes the undo stack cheap.
|
|
1058
|
+
*/
|
|
1059
|
+
function replaceAt(
|
|
1060
|
+
root: JsonNode,
|
|
1061
|
+
path: JsonPath,
|
|
1062
|
+
make: (existing: JsonNode) => JsonNode,
|
|
1063
|
+
): JsonNode | undefined {
|
|
1064
|
+
if (path.length === 0) {
|
|
1065
|
+
return make(root);
|
|
1066
|
+
}
|
|
1067
|
+
const [step, ...rest] = path;
|
|
1068
|
+
|
|
1069
|
+
if (root.kind === 'array' && typeof step === 'number') {
|
|
1070
|
+
const existing = root.items[step];
|
|
1071
|
+
if (existing === undefined) {
|
|
1072
|
+
return undefined;
|
|
1073
|
+
}
|
|
1074
|
+
const replacement = replaceAt(existing, rest, make);
|
|
1075
|
+
if (replacement === undefined) {
|
|
1076
|
+
return undefined;
|
|
1077
|
+
}
|
|
1078
|
+
const items = root.items.slice();
|
|
1079
|
+
items[step] = replacement;
|
|
1080
|
+
return { kind: 'array', items };
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (root.kind === 'object' && typeof step === 'string') {
|
|
1084
|
+
const index = root.entries.findIndex((entry) => entry.key === step);
|
|
1085
|
+
if (index < 0) {
|
|
1086
|
+
return undefined;
|
|
1087
|
+
}
|
|
1088
|
+
const replacement = replaceAt(root.entries[index]!.node, rest, make);
|
|
1089
|
+
if (replacement === undefined) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
1092
|
+
const entries = root.entries.slice();
|
|
1093
|
+
entries[index] = { key: step, node: replacement };
|
|
1094
|
+
return { kind: 'object', entries };
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
return undefined;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/** A neutral, non-destructive default value for each JSON type. */
|
|
1101
|
+
export function emptyNodeOfKind(kind: JsonKind): JsonNode {
|
|
1102
|
+
switch (kind) {
|
|
1103
|
+
case 'null':
|
|
1104
|
+
return { kind: 'null' };
|
|
1105
|
+
case 'boolean':
|
|
1106
|
+
return { kind: 'boolean', value: false };
|
|
1107
|
+
case 'number':
|
|
1108
|
+
return { kind: 'number', value: 0, literal: '0' };
|
|
1109
|
+
case 'string':
|
|
1110
|
+
return { kind: 'string', value: '' };
|
|
1111
|
+
case 'array':
|
|
1112
|
+
return { kind: 'array', items: [] };
|
|
1113
|
+
case 'object':
|
|
1114
|
+
return { kind: 'object', entries: [] };
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Interpret raw editor text as a value of `kind`.
|
|
1120
|
+
*
|
|
1121
|
+
* Returns `undefined` when the text cannot be read as that type — the caller
|
|
1122
|
+
* must then hold the edit as PENDING and show an error. It must never fall
|
|
1123
|
+
* back to a different type and must never discard the keystrokes, which is
|
|
1124
|
+
* exactly the failure mode of `Convert.stringToDataTypeValue` upstream.
|
|
1125
|
+
*/
|
|
1126
|
+
export function readScalar(kind: JsonKind, text: string): JsonNode | undefined {
|
|
1127
|
+
if (kind === 'string') {
|
|
1128
|
+
// Every possible text is a valid string, INCLUDING the empty one. An
|
|
1129
|
+
// empty string is a value, not a request to delete the property.
|
|
1130
|
+
return { kind: 'string', value: text };
|
|
1131
|
+
}
|
|
1132
|
+
if (kind === 'number') {
|
|
1133
|
+
const trimmed = text.trim();
|
|
1134
|
+
if (trimmed === '' || !NUMBER_PATTERN.test(trimmed)) {
|
|
1135
|
+
return undefined;
|
|
1136
|
+
}
|
|
1137
|
+
const match = NUMBER_PATTERN.exec(trimmed);
|
|
1138
|
+
if (!match || match[0] !== trimmed) {
|
|
1139
|
+
return undefined;
|
|
1140
|
+
}
|
|
1141
|
+
return { kind: 'number', value: Number(trimmed), literal: trimmed };
|
|
1142
|
+
}
|
|
1143
|
+
if (kind === 'boolean') {
|
|
1144
|
+
const trimmed = text.trim().toLowerCase();
|
|
1145
|
+
if (trimmed === 'true') {
|
|
1146
|
+
return { kind: 'boolean', value: true };
|
|
1147
|
+
}
|
|
1148
|
+
if (trimmed === 'false') {
|
|
1149
|
+
return { kind: 'boolean', value: false };
|
|
1150
|
+
}
|
|
1151
|
+
return undefined;
|
|
1152
|
+
}
|
|
1153
|
+
if (kind === 'null') {
|
|
1154
|
+
return text.trim() === '' || text.trim().toLowerCase() === 'null'
|
|
1155
|
+
? { kind: 'null' }
|
|
1156
|
+
: undefined;
|
|
1157
|
+
}
|
|
1158
|
+
return undefined;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/** Replace the value at `path` outright. */
|
|
1162
|
+
export function setNode(
|
|
1163
|
+
root: JsonNode,
|
|
1164
|
+
path: JsonPath,
|
|
1165
|
+
next: JsonNode,
|
|
1166
|
+
): EditResult {
|
|
1167
|
+
const updated = replaceAt(root, path, () => next);
|
|
1168
|
+
if (updated === undefined) {
|
|
1169
|
+
return rejected(
|
|
1170
|
+
root,
|
|
1171
|
+
'unexpected-token',
|
|
1172
|
+
'That value no longer exists.',
|
|
1173
|
+
path,
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
return ok(updated, `Set ${formatPath(path)}.`);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Change the TYPE of the value at `path`.
|
|
1181
|
+
*
|
|
1182
|
+
* Conversion preserves as much as it honestly can — `42` becomes `"42"`,
|
|
1183
|
+
* `"true"` becomes `true`, an object becomes an array of its values — and
|
|
1184
|
+
* where a conversion cannot preserve the content, the previous value stays on
|
|
1185
|
+
* the undo stack and the announcement says what was replaced. Nothing is
|
|
1186
|
+
* coerced behind the user's back.
|
|
1187
|
+
*/
|
|
1188
|
+
export function changeKind(
|
|
1189
|
+
root: JsonNode,
|
|
1190
|
+
path: JsonPath,
|
|
1191
|
+
kind: JsonKind,
|
|
1192
|
+
): EditResult {
|
|
1193
|
+
const existing = nodeAt(root, path);
|
|
1194
|
+
if (existing === undefined) {
|
|
1195
|
+
return rejected(
|
|
1196
|
+
root,
|
|
1197
|
+
'unexpected-token',
|
|
1198
|
+
'That value no longer exists.',
|
|
1199
|
+
path,
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
if (existing.kind === kind) {
|
|
1203
|
+
return noop(root, 'No change.');
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const converted = convertNode(existing, kind);
|
|
1207
|
+
const updated = replaceAt(root, path, () => converted.node);
|
|
1208
|
+
if (updated === undefined) {
|
|
1209
|
+
return rejected(
|
|
1210
|
+
root,
|
|
1211
|
+
'unexpected-token',
|
|
1212
|
+
'That value no longer exists.',
|
|
1213
|
+
path,
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
return {
|
|
1217
|
+
root: updated,
|
|
1218
|
+
changed: true,
|
|
1219
|
+
diagnostics: converted.lossy
|
|
1220
|
+
? [
|
|
1221
|
+
{
|
|
1222
|
+
severity: 'warning',
|
|
1223
|
+
code: 'pending-value',
|
|
1224
|
+
message: `Changing ${formatPath(path)} from ${existing.kind} to ${kind} could not preserve the previous content. Undo restores it.`,
|
|
1225
|
+
offset: 0,
|
|
1226
|
+
line: 1,
|
|
1227
|
+
column: 1,
|
|
1228
|
+
path,
|
|
1229
|
+
},
|
|
1230
|
+
]
|
|
1231
|
+
: [],
|
|
1232
|
+
announcement: `Changed ${formatPath(path)} to ${kind}.`,
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
function convertNode(
|
|
1237
|
+
node: JsonNode,
|
|
1238
|
+
kind: JsonKind,
|
|
1239
|
+
): { node: JsonNode; lossy: boolean } {
|
|
1240
|
+
if (kind === 'string') {
|
|
1241
|
+
switch (node.kind) {
|
|
1242
|
+
case 'string':
|
|
1243
|
+
return { node, lossy: false };
|
|
1244
|
+
case 'number':
|
|
1245
|
+
return { node: { kind: 'string', value: node.literal }, lossy: false };
|
|
1246
|
+
case 'boolean':
|
|
1247
|
+
return {
|
|
1248
|
+
node: { kind: 'string', value: String(node.value) },
|
|
1249
|
+
lossy: false,
|
|
1250
|
+
};
|
|
1251
|
+
case 'null':
|
|
1252
|
+
return { node: { kind: 'string', value: '' }, lossy: false };
|
|
1253
|
+
default:
|
|
1254
|
+
// Containers become their JSON text — reversible by hand, so not lost.
|
|
1255
|
+
return {
|
|
1256
|
+
node: { kind: 'string', value: stringifyJson(node, 0) },
|
|
1257
|
+
lossy: false,
|
|
1258
|
+
};
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
if (kind === 'number') {
|
|
1263
|
+
if (node.kind === 'number') {
|
|
1264
|
+
return { node, lossy: false };
|
|
1265
|
+
}
|
|
1266
|
+
if (node.kind === 'string') {
|
|
1267
|
+
const read = readScalar('number', node.value);
|
|
1268
|
+
if (read) {
|
|
1269
|
+
return { node: read, lossy: false };
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
if (node.kind === 'boolean') {
|
|
1273
|
+
return {
|
|
1274
|
+
node: {
|
|
1275
|
+
kind: 'number',
|
|
1276
|
+
value: node.value ? 1 : 0,
|
|
1277
|
+
literal: node.value ? '1' : '0',
|
|
1278
|
+
},
|
|
1279
|
+
lossy: false,
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
return { node: emptyNodeOfKind('number'), lossy: true };
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
if (kind === 'boolean') {
|
|
1286
|
+
if (node.kind === 'boolean') {
|
|
1287
|
+
return { node, lossy: false };
|
|
1288
|
+
}
|
|
1289
|
+
if (node.kind === 'string') {
|
|
1290
|
+
const read = readScalar('boolean', node.value);
|
|
1291
|
+
if (read) {
|
|
1292
|
+
return { node: read, lossy: false };
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (node.kind === 'number') {
|
|
1296
|
+
return {
|
|
1297
|
+
node: { kind: 'boolean', value: node.value !== 0 },
|
|
1298
|
+
lossy: false,
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
return { node: emptyNodeOfKind('boolean'), lossy: true };
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
if (kind === 'null') {
|
|
1305
|
+
return { node: { kind: 'null' }, lossy: node.kind !== 'null' };
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
if (kind === 'array') {
|
|
1309
|
+
if (node.kind === 'array') {
|
|
1310
|
+
return { node, lossy: false };
|
|
1311
|
+
}
|
|
1312
|
+
if (node.kind === 'object') {
|
|
1313
|
+
// Keys are dropped, values kept — say so.
|
|
1314
|
+
return {
|
|
1315
|
+
node: { kind: 'array', items: node.entries.map((entry) => entry.node) },
|
|
1316
|
+
lossy: true,
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
if (node.kind === 'null') {
|
|
1320
|
+
return { node: { kind: 'array', items: [] }, lossy: false };
|
|
1321
|
+
}
|
|
1322
|
+
return { node: { kind: 'array', items: [node] }, lossy: false };
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// kind === 'object'
|
|
1326
|
+
if (node.kind === 'object') {
|
|
1327
|
+
return { node, lossy: false };
|
|
1328
|
+
}
|
|
1329
|
+
if (node.kind === 'array') {
|
|
1330
|
+
return {
|
|
1331
|
+
node: {
|
|
1332
|
+
kind: 'object',
|
|
1333
|
+
entries: node.items.map((item, index) => ({
|
|
1334
|
+
key: String(index),
|
|
1335
|
+
node: item,
|
|
1336
|
+
})),
|
|
1337
|
+
},
|
|
1338
|
+
lossy: false,
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
if (node.kind === 'null') {
|
|
1342
|
+
return { node: { kind: 'object', entries: [] }, lossy: false };
|
|
1343
|
+
}
|
|
1344
|
+
return {
|
|
1345
|
+
node: { kind: 'object', entries: [{ key: 'value', node }] },
|
|
1346
|
+
lossy: false,
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/** Rename an object property, keeping its position in the key order. */
|
|
1351
|
+
export function renameKey(
|
|
1352
|
+
root: JsonNode,
|
|
1353
|
+
parentPath: JsonPath,
|
|
1354
|
+
index: number,
|
|
1355
|
+
nextKey: string,
|
|
1356
|
+
): EditResult {
|
|
1357
|
+
const parent = nodeAt(root, parentPath);
|
|
1358
|
+
if (parent === undefined || parent.kind !== 'object') {
|
|
1359
|
+
return rejected(
|
|
1360
|
+
root,
|
|
1361
|
+
'unexpected-token',
|
|
1362
|
+
'That object no longer exists.',
|
|
1363
|
+
parentPath,
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
const entry = parent.entries[index];
|
|
1367
|
+
if (entry === undefined) {
|
|
1368
|
+
return rejected(
|
|
1369
|
+
root,
|
|
1370
|
+
'unexpected-token',
|
|
1371
|
+
'That property no longer exists.',
|
|
1372
|
+
parentPath,
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
if (entry.key === nextKey) {
|
|
1376
|
+
return noop(root, 'No change.');
|
|
1377
|
+
}
|
|
1378
|
+
// An empty property name is legal JSON, so it is allowed here. What is NOT
|
|
1379
|
+
// allowed is silently overwriting a sibling.
|
|
1380
|
+
const clash = parent.entries.findIndex(
|
|
1381
|
+
(other, i) => i !== index && other.key === nextKey,
|
|
1382
|
+
);
|
|
1383
|
+
if (clash >= 0) {
|
|
1384
|
+
return rejected(
|
|
1385
|
+
root,
|
|
1386
|
+
'duplicate-key',
|
|
1387
|
+
`This object already has a property named "${nextKey}". Rename or remove that one first — renaming here would overwrite it.`,
|
|
1388
|
+
parentPath,
|
|
1389
|
+
);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
const entries = parent.entries.slice();
|
|
1393
|
+
entries[index] = { key: nextKey, node: entry.node };
|
|
1394
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1395
|
+
kind: 'object',
|
|
1396
|
+
entries,
|
|
1397
|
+
}));
|
|
1398
|
+
if (updated === undefined) {
|
|
1399
|
+
return rejected(
|
|
1400
|
+
root,
|
|
1401
|
+
'unexpected-token',
|
|
1402
|
+
'That object no longer exists.',
|
|
1403
|
+
parentPath,
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
return ok(updated, `Renamed property ${entry.key} to ${nextKey}.`);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/** Append a new property to an object. The key must not already exist. */
|
|
1410
|
+
export function addEntry(
|
|
1411
|
+
root: JsonNode,
|
|
1412
|
+
parentPath: JsonPath,
|
|
1413
|
+
key: string,
|
|
1414
|
+
node: JsonNode = { kind: 'null' },
|
|
1415
|
+
): EditResult {
|
|
1416
|
+
const parent = nodeAt(root, parentPath);
|
|
1417
|
+
if (parent === undefined || parent.kind !== 'object') {
|
|
1418
|
+
return rejected(
|
|
1419
|
+
root,
|
|
1420
|
+
'unexpected-token',
|
|
1421
|
+
'That object no longer exists.',
|
|
1422
|
+
parentPath,
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
if (parent.entries.some((entry) => entry.key === key)) {
|
|
1426
|
+
return rejected(
|
|
1427
|
+
root,
|
|
1428
|
+
'duplicate-key',
|
|
1429
|
+
`This object already has a property named "${key}".`,
|
|
1430
|
+
parentPath,
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
const entries = [...parent.entries, { key, node }];
|
|
1434
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1435
|
+
kind: 'object',
|
|
1436
|
+
entries,
|
|
1437
|
+
}));
|
|
1438
|
+
if (updated === undefined) {
|
|
1439
|
+
return rejected(
|
|
1440
|
+
root,
|
|
1441
|
+
'unexpected-token',
|
|
1442
|
+
'That object no longer exists.',
|
|
1443
|
+
parentPath,
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
return ok(updated, `Added property ${key}.`);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
/** Append a new item to an array. */
|
|
1450
|
+
export function addItem(
|
|
1451
|
+
root: JsonNode,
|
|
1452
|
+
parentPath: JsonPath,
|
|
1453
|
+
node: JsonNode = { kind: 'null' },
|
|
1454
|
+
): EditResult {
|
|
1455
|
+
const parent = nodeAt(root, parentPath);
|
|
1456
|
+
if (parent === undefined || parent.kind !== 'array') {
|
|
1457
|
+
return rejected(
|
|
1458
|
+
root,
|
|
1459
|
+
'unexpected-token',
|
|
1460
|
+
'That array no longer exists.',
|
|
1461
|
+
parentPath,
|
|
1462
|
+
);
|
|
1463
|
+
}
|
|
1464
|
+
const items = [...parent.items, node];
|
|
1465
|
+
const updated = replaceAt(root, parentPath, () => ({ kind: 'array', items }));
|
|
1466
|
+
if (updated === undefined) {
|
|
1467
|
+
return rejected(
|
|
1468
|
+
root,
|
|
1469
|
+
'unexpected-token',
|
|
1470
|
+
'That array no longer exists.',
|
|
1471
|
+
parentPath,
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
return ok(updated, `Added item ${items.length} of ${items.length}.`);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/** Remove a property or item. This is the ONLY way anything gets deleted. */
|
|
1478
|
+
export function removeAt(
|
|
1479
|
+
root: JsonNode,
|
|
1480
|
+
parentPath: JsonPath,
|
|
1481
|
+
index: number,
|
|
1482
|
+
): EditResult {
|
|
1483
|
+
const parent = nodeAt(root, parentPath);
|
|
1484
|
+
if (parent === undefined) {
|
|
1485
|
+
return rejected(
|
|
1486
|
+
root,
|
|
1487
|
+
'unexpected-token',
|
|
1488
|
+
'That container no longer exists.',
|
|
1489
|
+
parentPath,
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
if (parent.kind === 'array') {
|
|
1494
|
+
if (index < 0 || index >= parent.items.length) {
|
|
1495
|
+
return rejected(
|
|
1496
|
+
root,
|
|
1497
|
+
'unexpected-token',
|
|
1498
|
+
'That item no longer exists.',
|
|
1499
|
+
parentPath,
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
const items = parent.items.slice();
|
|
1503
|
+
items.splice(index, 1);
|
|
1504
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1505
|
+
kind: 'array',
|
|
1506
|
+
items,
|
|
1507
|
+
}));
|
|
1508
|
+
if (updated === undefined) {
|
|
1509
|
+
return rejected(
|
|
1510
|
+
root,
|
|
1511
|
+
'unexpected-token',
|
|
1512
|
+
'That array no longer exists.',
|
|
1513
|
+
parentPath,
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
return ok(updated, `Removed item ${index + 1}. ${items.length} remaining.`);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
if (parent.kind === 'object') {
|
|
1520
|
+
const entry = parent.entries[index];
|
|
1521
|
+
if (entry === undefined) {
|
|
1522
|
+
return rejected(
|
|
1523
|
+
root,
|
|
1524
|
+
'unexpected-token',
|
|
1525
|
+
'That property no longer exists.',
|
|
1526
|
+
parentPath,
|
|
1527
|
+
);
|
|
1528
|
+
}
|
|
1529
|
+
const entries = parent.entries.slice();
|
|
1530
|
+
entries.splice(index, 1);
|
|
1531
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1532
|
+
kind: 'object',
|
|
1533
|
+
entries,
|
|
1534
|
+
}));
|
|
1535
|
+
if (updated === undefined) {
|
|
1536
|
+
return rejected(
|
|
1537
|
+
root,
|
|
1538
|
+
'unexpected-token',
|
|
1539
|
+
'That object no longer exists.',
|
|
1540
|
+
parentPath,
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
return ok(
|
|
1544
|
+
updated,
|
|
1545
|
+
`Removed property ${entry.key}. ${entries.length} remaining.`,
|
|
1546
|
+
);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
return rejected(
|
|
1550
|
+
root,
|
|
1551
|
+
'unexpected-token',
|
|
1552
|
+
'Only arrays and objects have children.',
|
|
1553
|
+
parentPath,
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
/** Move an array item (or object property) from one position to another. */
|
|
1558
|
+
export function moveChild(
|
|
1559
|
+
root: JsonNode,
|
|
1560
|
+
parentPath: JsonPath,
|
|
1561
|
+
from: number,
|
|
1562
|
+
to: number,
|
|
1563
|
+
): EditResult {
|
|
1564
|
+
const parent = nodeAt(root, parentPath);
|
|
1565
|
+
if (parent === undefined) {
|
|
1566
|
+
return rejected(
|
|
1567
|
+
root,
|
|
1568
|
+
'unexpected-token',
|
|
1569
|
+
'That container no longer exists.',
|
|
1570
|
+
parentPath,
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
const length = childCount(parent);
|
|
1575
|
+
if (from < 0 || from >= length) {
|
|
1576
|
+
return rejected(
|
|
1577
|
+
root,
|
|
1578
|
+
'unexpected-token',
|
|
1579
|
+
'That item no longer exists.',
|
|
1580
|
+
parentPath,
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
const target = Math.max(0, Math.min(length - 1, to));
|
|
1584
|
+
if (target === from) {
|
|
1585
|
+
return noop(root, 'Already at the end of the list.');
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if (parent.kind === 'array') {
|
|
1589
|
+
const items = parent.items.slice();
|
|
1590
|
+
const [moved] = items.splice(from, 1);
|
|
1591
|
+
items.splice(target, 0, moved!);
|
|
1592
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1593
|
+
kind: 'array',
|
|
1594
|
+
items,
|
|
1595
|
+
}));
|
|
1596
|
+
if (updated === undefined) {
|
|
1597
|
+
return rejected(
|
|
1598
|
+
root,
|
|
1599
|
+
'unexpected-token',
|
|
1600
|
+
'That array no longer exists.',
|
|
1601
|
+
parentPath,
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
return ok(
|
|
1605
|
+
updated,
|
|
1606
|
+
`Moved item from position ${from + 1} to ${target + 1} of ${length}.`,
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
if (parent.kind === 'object') {
|
|
1611
|
+
const entries = parent.entries.slice();
|
|
1612
|
+
const [moved] = entries.splice(from, 1);
|
|
1613
|
+
entries.splice(target, 0, moved!);
|
|
1614
|
+
const updated = replaceAt(root, parentPath, () => ({
|
|
1615
|
+
kind: 'object',
|
|
1616
|
+
entries,
|
|
1617
|
+
}));
|
|
1618
|
+
if (updated === undefined) {
|
|
1619
|
+
return rejected(
|
|
1620
|
+
root,
|
|
1621
|
+
'unexpected-token',
|
|
1622
|
+
'That object no longer exists.',
|
|
1623
|
+
parentPath,
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
return ok(
|
|
1627
|
+
updated,
|
|
1628
|
+
`Moved property ${moved!.key} from position ${from + 1} to ${target + 1} of ${length}.`,
|
|
1629
|
+
);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
return rejected(
|
|
1633
|
+
root,
|
|
1634
|
+
'unexpected-token',
|
|
1635
|
+
'Only arrays and objects have children.',
|
|
1636
|
+
parentPath,
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/* ------------------------------------------------------------------ *
|
|
1641
|
+
* Search
|
|
1642
|
+
* ------------------------------------------------------------------ */
|
|
1643
|
+
|
|
1644
|
+
export interface JsonMatch {
|
|
1645
|
+
readonly path: JsonPath;
|
|
1646
|
+
/** Whether the hit was on the property name, the value, or both. */
|
|
1647
|
+
readonly where: 'key' | 'value' | 'both';
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/** A short, single-line rendering of a node, for collapsed previews and search. */
|
|
1651
|
+
export function previewOf(node: JsonNode, limit = 48): string {
|
|
1652
|
+
let text: string;
|
|
1653
|
+
switch (node.kind) {
|
|
1654
|
+
case 'null':
|
|
1655
|
+
text = 'null';
|
|
1656
|
+
break;
|
|
1657
|
+
case 'boolean':
|
|
1658
|
+
text = node.value ? 'true' : 'false';
|
|
1659
|
+
break;
|
|
1660
|
+
case 'number':
|
|
1661
|
+
text = node.literal;
|
|
1662
|
+
break;
|
|
1663
|
+
case 'string':
|
|
1664
|
+
text = node.value;
|
|
1665
|
+
break;
|
|
1666
|
+
case 'array':
|
|
1667
|
+
text =
|
|
1668
|
+
node.items.length === 1
|
|
1669
|
+
? '1 item'
|
|
1670
|
+
: String(node.items.length) + ' items';
|
|
1671
|
+
break;
|
|
1672
|
+
case 'object':
|
|
1673
|
+
text =
|
|
1674
|
+
node.entries.length === 1
|
|
1675
|
+
? '1 property'
|
|
1676
|
+
: String(node.entries.length) + ' properties';
|
|
1677
|
+
break;
|
|
1678
|
+
}
|
|
1679
|
+
return text.length > limit ? text.slice(0, limit - 1) + '…' : text;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
/**
|
|
1683
|
+
* Find every node whose property name or scalar value contains `query`
|
|
1684
|
+
* (case-insensitive). Returns paths in document order.
|
|
1685
|
+
*/
|
|
1686
|
+
export function searchJson(
|
|
1687
|
+
root: JsonNode,
|
|
1688
|
+
query: string,
|
|
1689
|
+
): ReadonlyArray<JsonMatch> {
|
|
1690
|
+
const needle = query.trim().toLowerCase();
|
|
1691
|
+
if (needle === '') {
|
|
1692
|
+
return [];
|
|
1693
|
+
}
|
|
1694
|
+
const results: JsonMatch[] = [];
|
|
1695
|
+
|
|
1696
|
+
const scalarText = (node: JsonNode): string | undefined => {
|
|
1697
|
+
switch (node.kind) {
|
|
1698
|
+
case 'null':
|
|
1699
|
+
return 'null';
|
|
1700
|
+
case 'boolean':
|
|
1701
|
+
return node.value ? 'true' : 'false';
|
|
1702
|
+
case 'number':
|
|
1703
|
+
return node.literal;
|
|
1704
|
+
case 'string':
|
|
1705
|
+
return node.value;
|
|
1706
|
+
default:
|
|
1707
|
+
return undefined;
|
|
1708
|
+
}
|
|
1709
|
+
};
|
|
1710
|
+
|
|
1711
|
+
const walk = (
|
|
1712
|
+
node: JsonNode,
|
|
1713
|
+
path: JsonPath,
|
|
1714
|
+
key: string | undefined,
|
|
1715
|
+
): void => {
|
|
1716
|
+
const keyHit = key !== undefined && key.toLowerCase().includes(needle);
|
|
1717
|
+
const text = scalarText(node);
|
|
1718
|
+
const valueHit = text !== undefined && text.toLowerCase().includes(needle);
|
|
1719
|
+
if (keyHit && valueHit) {
|
|
1720
|
+
results.push({ path, where: 'both' });
|
|
1721
|
+
} else if (keyHit) {
|
|
1722
|
+
results.push({ path, where: 'key' });
|
|
1723
|
+
} else if (valueHit) {
|
|
1724
|
+
results.push({ path, where: 'value' });
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
if (node.kind === 'array') {
|
|
1728
|
+
node.items.forEach((item, index) =>
|
|
1729
|
+
walk(item, [...path, index], undefined),
|
|
1730
|
+
);
|
|
1731
|
+
} else if (node.kind === 'object') {
|
|
1732
|
+
node.entries.forEach((entry) =>
|
|
1733
|
+
walk(entry.node, [...path, entry.key], entry.key),
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
|
|
1738
|
+
walk(root, [], undefined);
|
|
1739
|
+
return results;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
/** Every ancestor path of `path`, shortest first — what a search hit must expand. */
|
|
1743
|
+
export function ancestorPaths(path: JsonPath): ReadonlyArray<JsonPath> {
|
|
1744
|
+
const out: JsonPath[] = [];
|
|
1745
|
+
for (let i = 0; i < path.length; i++) {
|
|
1746
|
+
out.push(path.slice(0, i));
|
|
1747
|
+
}
|
|
1748
|
+
return out;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
/* ------------------------------------------------------------------ *
|
|
1752
|
+
* Undo history
|
|
1753
|
+
* ------------------------------------------------------------------ */
|
|
1754
|
+
|
|
1755
|
+
/**
|
|
1756
|
+
* An immutable undo stack. Because every edit returns a new root with
|
|
1757
|
+
* structural sharing, a "snapshot" is just a root reference — so history
|
|
1758
|
+
* costs one pointer per step, not a deep clone.
|
|
1759
|
+
*
|
|
1760
|
+
* `limit` caps the stack so a long editing session cannot grow without bound.
|
|
1761
|
+
*/
|
|
1762
|
+
export class JsonHistory {
|
|
1763
|
+
readonly #limit: number;
|
|
1764
|
+
#past: JsonNode[] = [];
|
|
1765
|
+
#future: JsonNode[] = [];
|
|
1766
|
+
|
|
1767
|
+
constructor(limit = 100) {
|
|
1768
|
+
this.#limit = Math.max(1, limit);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
get canUndo(): boolean {
|
|
1772
|
+
return this.#past.length > 0;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
get canRedo(): boolean {
|
|
1776
|
+
return this.#future.length > 0;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
get depth(): number {
|
|
1780
|
+
return this.#past.length;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/** Record the root as it was BEFORE an edit. Clears the redo branch. */
|
|
1784
|
+
push(previous: JsonNode): void {
|
|
1785
|
+
this.#past.push(previous);
|
|
1786
|
+
if (this.#past.length > this.#limit) {
|
|
1787
|
+
this.#past.shift();
|
|
1788
|
+
}
|
|
1789
|
+
this.#future = [];
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
/** Step back. `current` is the root being left, which becomes redoable. */
|
|
1793
|
+
undo(current: JsonNode): JsonNode | undefined {
|
|
1794
|
+
const previous = this.#past.pop();
|
|
1795
|
+
if (previous === undefined) {
|
|
1796
|
+
return undefined;
|
|
1797
|
+
}
|
|
1798
|
+
this.#future.push(current);
|
|
1799
|
+
return previous;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/** Step forward. `current` is the root being left, which becomes undoable. */
|
|
1803
|
+
redo(current: JsonNode): JsonNode | undefined {
|
|
1804
|
+
const next = this.#future.pop();
|
|
1805
|
+
if (next === undefined) {
|
|
1806
|
+
return undefined;
|
|
1807
|
+
}
|
|
1808
|
+
this.#past.push(current);
|
|
1809
|
+
return next;
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
clear(): void {
|
|
1813
|
+
this.#past = [];
|
|
1814
|
+
this.#future = [];
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
/* ------------------------------------------------------------------ *
|
|
1819
|
+
* Flattening — the tree view's render list
|
|
1820
|
+
* ------------------------------------------------------------------ */
|
|
1821
|
+
|
|
1822
|
+
export interface FlatRow {
|
|
1823
|
+
readonly path: JsonPath;
|
|
1824
|
+
/** Stable id for DOM ids and `{{#each key=}}`. */
|
|
1825
|
+
readonly id: string;
|
|
1826
|
+
/** Property name, array index label, or `undefined` for the root. */
|
|
1827
|
+
readonly label: string | undefined;
|
|
1828
|
+
/** Index within the parent container; `-1` for the root. */
|
|
1829
|
+
readonly index: number;
|
|
1830
|
+
readonly parentPath: JsonPath;
|
|
1831
|
+
/** `pathKey` of the parent row, or `undefined` for the root. */
|
|
1832
|
+
readonly parentId: string | undefined;
|
|
1833
|
+
readonly node: JsonNode;
|
|
1834
|
+
/** ARIA level, 1-based. */
|
|
1835
|
+
readonly level: number;
|
|
1836
|
+
/** ARIA position among siblings, 1-based. */
|
|
1837
|
+
readonly posinset: number;
|
|
1838
|
+
/** ARIA sibling count, including any overflow notice row. */
|
|
1839
|
+
readonly setsize: number;
|
|
1840
|
+
readonly expandable: boolean;
|
|
1841
|
+
readonly expanded: boolean;
|
|
1842
|
+
/** TRUE when this row is a truncation notice rather than a value. */
|
|
1843
|
+
readonly overflow?: { shown: number; total: number };
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
export interface FlattenOptions {
|
|
1847
|
+
/** Paths (as `pathKey` strings) that are currently expanded. */
|
|
1848
|
+
readonly expanded: ReadonlySet<string>;
|
|
1849
|
+
/**
|
|
1850
|
+
* Maximum children rendered per container before truncating. Truncation,
|
|
1851
|
+
* not virtualization — virtualization needs measurement and this realm
|
|
1852
|
+
* forbids the timers that would drive it, and an honest "showing N of M"
|
|
1853
|
+
* beats a scroll position that lies.
|
|
1854
|
+
*/
|
|
1855
|
+
readonly pageSize: number;
|
|
1856
|
+
/** Per-container overrides raising the window, keyed by `pathKey`. */
|
|
1857
|
+
readonly revealed?: ReadonlyMap<string, number>;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
/**
|
|
1861
|
+
* Walk the document into the flat, ordered row list the treeview renders.
|
|
1862
|
+
* Only expanded containers contribute children, so the cost is proportional
|
|
1863
|
+
* to what is on screen.
|
|
1864
|
+
*/
|
|
1865
|
+
export function flattenJson(
|
|
1866
|
+
root: JsonNode,
|
|
1867
|
+
options: FlattenOptions,
|
|
1868
|
+
): ReadonlyArray<FlatRow> {
|
|
1869
|
+
const rows: FlatRow[] = [];
|
|
1870
|
+
|
|
1871
|
+
const visit = (
|
|
1872
|
+
node: JsonNode,
|
|
1873
|
+
path: JsonPath,
|
|
1874
|
+
parentPath: JsonPath,
|
|
1875
|
+
parentId: string | undefined,
|
|
1876
|
+
label: string | undefined,
|
|
1877
|
+
index: number,
|
|
1878
|
+
level: number,
|
|
1879
|
+
posinset: number,
|
|
1880
|
+
setsize: number,
|
|
1881
|
+
): void => {
|
|
1882
|
+
const key = pathKey(path);
|
|
1883
|
+
const id = key === '' ? '$' : key;
|
|
1884
|
+
const expandable = isContainer(node) && childCount(node) > 0;
|
|
1885
|
+
const expanded = expandable && options.expanded.has(key);
|
|
1886
|
+
|
|
1887
|
+
rows.push({
|
|
1888
|
+
path,
|
|
1889
|
+
id,
|
|
1890
|
+
label,
|
|
1891
|
+
index,
|
|
1892
|
+
parentPath,
|
|
1893
|
+
parentId,
|
|
1894
|
+
node,
|
|
1895
|
+
level,
|
|
1896
|
+
posinset,
|
|
1897
|
+
setsize,
|
|
1898
|
+
expandable,
|
|
1899
|
+
expanded,
|
|
1900
|
+
});
|
|
1901
|
+
|
|
1902
|
+
if (!expanded) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
const total = childCount(node);
|
|
1907
|
+
const window = Math.max(options.pageSize, options.revealed?.get(key) ?? 0);
|
|
1908
|
+
const shown = Math.min(total, window);
|
|
1909
|
+
// The overflow notice is a focusable row, so it counts toward `setsize`.
|
|
1910
|
+
const siblings = shown < total ? shown + 1 : shown;
|
|
1911
|
+
|
|
1912
|
+
if (node.kind === 'array') {
|
|
1913
|
+
for (let i = 0; i < shown; i++) {
|
|
1914
|
+
visit(
|
|
1915
|
+
node.items[i]!,
|
|
1916
|
+
[...path, i],
|
|
1917
|
+
path,
|
|
1918
|
+
id,
|
|
1919
|
+
String(i),
|
|
1920
|
+
i,
|
|
1921
|
+
level + 1,
|
|
1922
|
+
i + 1,
|
|
1923
|
+
siblings,
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
} else if (node.kind === 'object') {
|
|
1927
|
+
for (let i = 0; i < shown; i++) {
|
|
1928
|
+
const entry = node.entries[i]!;
|
|
1929
|
+
visit(
|
|
1930
|
+
entry.node,
|
|
1931
|
+
[...path, entry.key],
|
|
1932
|
+
path,
|
|
1933
|
+
id,
|
|
1934
|
+
entry.key,
|
|
1935
|
+
i,
|
|
1936
|
+
level + 1,
|
|
1937
|
+
i + 1,
|
|
1938
|
+
siblings,
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
if (shown < total) {
|
|
1944
|
+
rows.push({
|
|
1945
|
+
path: [...path, '@@overflow'],
|
|
1946
|
+
id: id + '@@overflow',
|
|
1947
|
+
label: undefined,
|
|
1948
|
+
index: shown,
|
|
1949
|
+
parentPath: path,
|
|
1950
|
+
parentId: id,
|
|
1951
|
+
node: { kind: 'null' },
|
|
1952
|
+
level: level + 1,
|
|
1953
|
+
posinset: siblings,
|
|
1954
|
+
setsize: siblings,
|
|
1955
|
+
expandable: false,
|
|
1956
|
+
expanded: false,
|
|
1957
|
+
overflow: { shown, total },
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1962
|
+
visit(root, [], [], undefined, undefined, -1, 1, 1, 1);
|
|
1963
|
+
return rows;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
/** Every container path in the document — what "expand all" needs. */
|
|
1967
|
+
export function allContainerPaths(root: JsonNode): ReadonlyArray<string> {
|
|
1968
|
+
const out: string[] = [];
|
|
1969
|
+
const walk = (node: JsonNode, path: JsonPath): void => {
|
|
1970
|
+
if (isContainer(node) && childCount(node) > 0) {
|
|
1971
|
+
out.push(pathKey(path));
|
|
1972
|
+
}
|
|
1973
|
+
if (node.kind === 'array') {
|
|
1974
|
+
node.items.forEach((item, index) => walk(item, [...path, index]));
|
|
1975
|
+
} else if (node.kind === 'object') {
|
|
1976
|
+
node.entries.forEach((entry) => walk(entry.node, [...path, entry.key]));
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
walk(root, []);
|
|
1980
|
+
return out;
|
|
1981
|
+
}
|