termcast 1.3.21 → 1.3.25
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/dist/ai.d.ts +104 -0
- package/dist/ai.d.ts.map +1 -0
- package/dist/ai.js +135 -0
- package/dist/ai.js.map +1 -0
- package/dist/apis/browser-extension.d.ts +18 -0
- package/dist/apis/browser-extension.d.ts.map +1 -0
- package/dist/apis/browser-extension.js +14 -0
- package/dist/apis/browser-extension.js.map +1 -0
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +4 -7
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/oauth.d.ts.map +1 -1
- package/dist/apis/oauth.js +5 -1
- package/dist/apis/oauth.js.map +1 -1
- package/dist/apis/preferences.d.ts.map +1 -1
- package/dist/apis/preferences.js +38 -19
- package/dist/apis/preferences.js.map +1 -1
- package/dist/apis/toast.d.ts +5 -0
- package/dist/apis/toast.d.ts.map +1 -1
- package/dist/apis/toast.js +7 -43
- package/dist/apis/toast.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +3 -1
- package/dist/build.js.map +1 -1
- package/dist/cache.d.ts +32 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +205 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +87 -41
- package/dist/cli.js.map +1 -1
- package/dist/clipboard.d.ts +36 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +154 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/compile.d.ts +2 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +31 -12
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.d.ts.map +1 -1
- package/dist/components/actions.js +56 -30
- package/dist/components/actions.js.map +1 -1
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +4 -0
- package/dist/components/detail.js.map +1 -1
- package/dist/components/dropdown.d.ts.map +1 -1
- package/dist/components/dropdown.js +38 -15
- package/dist/components/dropdown.js.map +1 -1
- package/dist/components/extension-preferences.d.ts.map +1 -1
- package/dist/components/extension-preferences.js +40 -13
- package/dist/components/extension-preferences.js.map +1 -1
- package/dist/components/form/checkbox.d.ts.map +1 -1
- package/dist/components/form/checkbox.js +5 -3
- package/dist/components/form/checkbox.js.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +5 -3
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/description.d.ts.map +1 -1
- package/dist/components/form/description.js +2 -2
- package/dist/components/form/description.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +84 -80
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/file-autocomplete.d.ts +3 -6
- package/dist/components/form/file-autocomplete.d.ts.map +1 -1
- package/dist/components/form/file-autocomplete.js +61 -66
- package/dist/components/form/file-autocomplete.js.map +1 -1
- package/dist/components/form/file-picker.d.ts.map +1 -1
- package/dist/components/form/file-picker.js +33 -30
- package/dist/components/form/file-picker.js.map +1 -1
- package/dist/components/form/form-end.d.ts.map +1 -1
- package/dist/components/form/form-end.js +21 -1
- package/dist/components/form/form-end.js.map +1 -1
- package/dist/components/form/form-type-only.d.ts +174 -0
- package/dist/components/form/form-type-only.d.ts.map +1 -0
- package/dist/components/form/form-type-only.js +2 -0
- package/dist/components/form/form-type-only.js.map +1 -0
- package/dist/components/form/index.d.ts +3 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +104 -30
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/password-field.d.ts.map +1 -1
- package/dist/components/form/password-field.js +5 -3
- package/dist/components/form/password-field.js.map +1 -1
- package/dist/components/form/text-area.d.ts.map +1 -1
- package/dist/components/form/text-area.js +5 -3
- package/dist/components/form/text-area.js.map +1 -1
- package/dist/components/form/text-field.d.ts.map +1 -1
- package/dist/components/form/text-field.js +6 -4
- package/dist/components/form/text-field.js.map +1 -1
- package/dist/components/form/types.d.ts +5 -0
- package/dist/components/form/types.d.ts.map +1 -1
- package/dist/components/form/use-form-handling.d.ts +4 -0
- package/dist/components/form/use-form-handling.d.ts.map +1 -0
- package/dist/components/form/use-form-handling.js +37 -0
- package/dist/components/form/use-form-handling.js.map +1 -0
- package/dist/components/form/with-left-border.d.ts +2 -1
- package/dist/components/form/with-left-border.d.ts.map +1 -1
- package/dist/components/form/with-left-border.js +27 -3
- package/dist/components/form/with-left-border.js.map +1 -1
- package/dist/components/icon.d.ts +1 -0
- package/dist/components/icon.d.ts.map +1 -1
- package/dist/components/icon.js +24 -8
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +2 -2
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +155 -70
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.d.ts.map +1 -1
- package/dist/components/loading-bar.js +2 -2
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts +8 -0
- package/dist/components/loading-text.d.ts.map +1 -0
- package/dist/components/loading-text.js +58 -0
- package/dist/components/loading-text.js.map +1 -0
- package/dist/descendants.js +1 -1
- package/dist/descendants.js.map +1 -1
- package/dist/dev-ui.d.ts +7 -0
- package/dist/dev-ui.d.ts.map +1 -0
- package/dist/dev-ui.js +118 -0
- package/dist/dev-ui.js.map +1 -0
- package/dist/environment.d.ts +63 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +189 -0
- package/dist/environment.js.map +1 -0
- package/dist/examples/datepicker.d.ts +2 -0
- package/dist/examples/datepicker.d.ts.map +1 -0
- package/dist/examples/datepicker.js +344 -0
- package/dist/examples/datepicker.js.map +1 -0
- package/dist/examples/file-autocomplete.vitest.d.ts +2 -0
- package/dist/examples/file-autocomplete.vitest.d.ts.map +1 -0
- package/dist/examples/file-autocomplete.vitest.js +223 -0
- package/dist/examples/file-autocomplete.vitest.js.map +1 -0
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts +2 -0
- package/dist/examples/form-basic-arrow-keys.vitest.d.ts.map +1 -0
- package/dist/examples/form-basic-arrow-keys.vitest.js +46 -0
- package/dist/examples/form-basic-arrow-keys.vitest.js.map +1 -0
- package/dist/examples/form-basic.vitest.d.ts +2 -0
- package/dist/examples/form-basic.vitest.d.ts.map +1 -0
- package/dist/examples/form-basic.vitest.js +630 -0
- package/dist/examples/form-basic.vitest.js.map +1 -0
- package/dist/examples/form-dropdown-with-sections.d.ts +2 -0
- package/dist/examples/form-dropdown-with-sections.d.ts.map +1 -0
- package/dist/examples/form-dropdown-with-sections.js +13 -0
- package/dist/examples/form-dropdown-with-sections.js.map +1 -0
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts +2 -0
- package/dist/examples/form-dropdown-with-sections.vitest.d.ts.map +1 -0
- package/dist/examples/form-dropdown-with-sections.vitest.js +75 -0
- package/dist/examples/form-dropdown-with-sections.vitest.js.map +1 -0
- package/dist/examples/form-dropdown.vitest.d.ts +2 -0
- package/dist/examples/form-dropdown.vitest.d.ts.map +1 -0
- package/dist/examples/form-dropdown.vitest.js +854 -0
- package/dist/examples/form-dropdown.vitest.js.map +1 -0
- package/dist/examples/form-multiselect-dropdown.d.ts +2 -0
- package/dist/examples/form-multiselect-dropdown.d.ts.map +1 -0
- package/dist/examples/form-multiselect-dropdown.js +13 -0
- package/dist/examples/form-multiselect-dropdown.js.map +1 -0
- package/dist/examples/form-scroll.d.ts.map +1 -1
- package/dist/examples/form-scroll.js +7 -1
- package/dist/examples/form-scroll.js.map +1 -1
- package/dist/examples/form-scroll.vitest.d.ts +2 -0
- package/dist/examples/form-scroll.vitest.d.ts.map +1 -0
- package/dist/examples/form-scroll.vitest.js +211 -0
- package/dist/examples/form-scroll.vitest.js.map +1 -0
- package/dist/examples/form-tagpicker.vitest.d.ts +2 -0
- package/dist/examples/form-tagpicker.vitest.d.ts.map +1 -0
- package/dist/examples/form-tagpicker.vitest.js +736 -0
- package/dist/examples/form-tagpicker.vitest.js.map +1 -0
- package/dist/examples/internal/descendants-filtering.js +1 -1
- package/dist/examples/internal/descendants-filtering.js.map +1 -1
- package/dist/examples/internal/descendants.js +1 -1
- package/dist/examples/internal/descendants.js.map +1 -1
- package/dist/examples/internal/nested-boxes.d.ts +2 -0
- package/dist/examples/internal/nested-boxes.d.ts.map +1 -0
- package/dist/examples/internal/nested-boxes.js +7 -0
- package/dist/examples/internal/nested-boxes.js.map +1 -0
- package/dist/examples/internal/rhf-custom-ref.js +2 -2
- package/dist/examples/internal/rhf-custom-ref.js.map +1 -1
- package/dist/examples/internal/scrollbox-demo.js +3 -22
- package/dist/examples/internal/scrollbox-demo.js.map +1 -1
- package/dist/examples/internal/scrollbox-descendants.d.ts +2 -0
- package/dist/examples/internal/scrollbox-descendants.d.ts.map +1 -0
- package/dist/examples/internal/scrollbox-descendants.js +83 -0
- package/dist/examples/internal/scrollbox-descendants.js.map +1 -0
- package/dist/examples/internal/scrollbox-with-descendants.js +4 -8
- package/dist/examples/internal/scrollbox-with-descendants.js.map +1 -1
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts +2 -0
- package/dist/examples/internal/simple-scrollbox.vitest.d.ts.map +1 -0
- package/dist/examples/internal/simple-scrollbox.vitest.js +96 -0
- package/dist/examples/internal/simple-scrollbox.vitest.js.map +1 -0
- package/dist/examples/internal/unicode-square-repro.d.ts +2 -0
- package/dist/examples/internal/unicode-square-repro.d.ts.map +1 -0
- package/dist/examples/internal/unicode-square-repro.js +7 -0
- package/dist/examples/internal/unicode-square-repro.js.map +1 -0
- package/dist/examples/list-detail-metadata.d.ts +2 -0
- package/dist/examples/list-detail-metadata.d.ts.map +1 -0
- package/dist/examples/list-detail-metadata.js +8 -0
- package/dist/examples/list-detail-metadata.js.map +1 -0
- package/dist/examples/list-dropdown-default.vitest.d.ts +2 -0
- package/dist/examples/list-dropdown-default.vitest.d.ts.map +1 -0
- package/dist/examples/list-dropdown-default.vitest.js +234 -0
- package/dist/examples/list-dropdown-default.vitest.js.map +1 -0
- package/dist/examples/list-fetch-data.vitest.d.ts +2 -0
- package/dist/examples/list-fetch-data.vitest.d.ts.map +1 -0
- package/dist/examples/list-fetch-data.vitest.js +111 -0
- package/dist/examples/list-fetch-data.vitest.js.map +1 -0
- package/dist/examples/list-filter-navigation.d.ts +2 -0
- package/dist/examples/list-filter-navigation.d.ts.map +1 -0
- package/dist/examples/list-filter-navigation.js +8 -0
- package/dist/examples/list-filter-navigation.js.map +1 -0
- package/dist/examples/list-scrollbox.vitest.d.ts +2 -0
- package/dist/examples/list-scrollbox.vitest.d.ts.map +1 -0
- package/dist/examples/list-scrollbox.vitest.js +93 -0
- package/dist/examples/list-scrollbox.vitest.js.map +1 -0
- package/dist/examples/list-with-detail-long.d.ts +2 -0
- package/dist/examples/list-with-detail-long.d.ts.map +1 -0
- package/dist/examples/list-with-detail-long.js +53 -0
- package/dist/examples/list-with-detail-long.js.map +1 -0
- package/dist/examples/list-with-detail.vitest.d.ts +2 -0
- package/dist/examples/list-with-detail.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-detail.vitest.js +434 -0
- package/dist/examples/list-with-detail.vitest.js.map +1 -0
- package/dist/examples/list-with-dropdown.vitest.d.ts +2 -0
- package/dist/examples/list-with-dropdown.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-dropdown.vitest.js +337 -0
- package/dist/examples/list-with-dropdown.vitest.js.map +1 -0
- package/dist/examples/list-with-sections.js +5 -1
- package/dist/examples/list-with-sections.js.map +1 -1
- package/dist/examples/list-with-sections.vitest.d.ts +2 -0
- package/dist/examples/list-with-sections.vitest.d.ts.map +1 -0
- package/dist/examples/list-with-sections.vitest.js +601 -0
- package/dist/examples/list-with-sections.vitest.js.map +1 -0
- package/dist/examples/scrollbox-vertical-centering.d.ts +6 -0
- package/dist/examples/scrollbox-vertical-centering.d.ts.map +1 -0
- package/dist/examples/scrollbox-vertical-centering.js +17 -0
- package/dist/examples/scrollbox-vertical-centering.js.map +1 -0
- package/dist/examples/simple-file-picker.vitest.d.ts +2 -0
- package/dist/examples/simple-file-picker.vitest.d.ts.map +1 -0
- package/dist/examples/simple-file-picker.vitest.js +678 -0
- package/dist/examples/simple-file-picker.vitest.js.map +1 -0
- package/dist/examples/simple-grid.vitest.d.ts +2 -0
- package/dist/examples/simple-grid.vitest.d.ts.map +1 -0
- package/dist/examples/simple-grid.vitest.js +521 -0
- package/dist/examples/simple-grid.vitest.js.map +1 -0
- package/dist/examples/simple-navigation.js +10 -4
- package/dist/examples/simple-navigation.js.map +1 -1
- package/dist/examples/simple-navigation.vitest.d.ts +2 -0
- package/dist/examples/simple-navigation.vitest.d.ts.map +1 -0
- package/dist/examples/simple-navigation.vitest.js +718 -0
- package/dist/examples/simple-navigation.vitest.js.map +1 -0
- package/dist/examples/store.vitest.d.ts +2 -0
- package/dist/examples/store.vitest.d.ts.map +1 -0
- package/dist/examples/store.vitest.js +69 -0
- package/dist/examples/store.vitest.js.map +1 -0
- package/dist/examples/toast-variations.d.ts +2 -0
- package/dist/examples/toast-variations.d.ts.map +1 -0
- package/dist/examples/toast-variations.js +122 -0
- package/dist/examples/toast-variations.js.map +1 -0
- package/dist/extensions/dev.d.ts +4 -2
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +61 -10
- package/dist/extensions/dev.js.map +1 -1
- package/dist/extensions/dev.vitest.d.ts +2 -0
- package/dist/extensions/dev.vitest.d.ts.map +1 -0
- package/dist/extensions/dev.vitest.js +197 -0
- package/dist/extensions/dev.vitest.js.map +1 -0
- package/dist/extensions/home.d.ts.map +1 -1
- package/dist/extensions/home.js +3 -0
- package/dist/extensions/home.js.map +1 -1
- package/dist/home-command.d.ts +8 -0
- package/dist/home-command.d.ts.map +1 -0
- package/dist/home-command.js +181 -0
- package/dist/home-command.js.map +1 -0
- package/dist/hover-repro.d.ts +2 -0
- package/dist/hover-repro.d.ts.map +1 -0
- package/dist/hover-repro.js +20 -0
- package/dist/hover-repro.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/dialog.d.ts +1 -0
- package/dist/internal/dialog.d.ts.map +1 -1
- package/dist/internal/dialog.js +27 -18
- package/dist/internal/dialog.js.map +1 -1
- package/dist/internal/navigation.d.ts +9 -1
- package/dist/internal/navigation.d.ts.map +1 -1
- package/dist/internal/navigation.js +5 -5
- package/dist/internal/navigation.js.map +1 -1
- package/dist/internal/offscreen.d.ts +6 -0
- package/dist/internal/offscreen.d.ts.map +1 -0
- package/dist/internal/offscreen.js +10 -0
- package/dist/internal/offscreen.js.map +1 -0
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +2 -2
- package/dist/internal/providers.js.map +1 -1
- package/dist/internal/scrollbox.d.ts +1 -10
- package/dist/internal/scrollbox.d.ts.map +1 -1
- package/dist/internal/scrollbox.js +2 -1
- package/dist/internal/scrollbox.js.map +1 -1
- package/dist/localstorage.d.ts +13 -0
- package/dist/localstorage.d.ts.map +1 -0
- package/dist/localstorage.js +190 -0
- package/dist/localstorage.js.map +1 -0
- package/dist/oauth.d.ts +142 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +551 -0
- package/dist/oauth.js.map +1 -0
- package/dist/preferences.d.ts +23 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +105 -0
- package/dist/preferences.js.map +1 -0
- package/dist/release.d.ts +1 -1
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +29 -33
- package/dist/release.js.map +1 -1
- package/dist/state.d.ts +2 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +3 -0
- package/dist/state.js.map +1 -1
- package/dist/store.d.ts +21 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +84 -0
- package/dist/store.js.map +1 -0
- package/dist/swift-loader.d.ts +3 -0
- package/dist/swift-loader.d.ts.map +1 -0
- package/dist/swift-loader.js +193 -0
- package/dist/swift-loader.js.map +1 -0
- package/dist/swift-runtime.d.ts +2 -0
- package/dist/swift-runtime.d.ts.map +1 -0
- package/dist/swift-runtime.js +27 -0
- package/dist/swift-runtime.js.map +1 -0
- package/dist/toast.d.ts +44 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +221 -0
- package/dist/toast.js.map +1 -0
- package/dist/utils/file-system.d.ts +9 -0
- package/dist/utils/file-system.d.ts.map +1 -1
- package/dist/utils/file-system.js +49 -0
- package/dist/utils/file-system.js.map +1 -1
- package/dist/utils/run-command.d.ts +26 -1
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +53 -4
- package/dist/utils/run-command.js.map +1 -1
- package/dist/window.d.ts +12 -0
- package/dist/window.d.ts.map +1 -0
- package/dist/window.js +48 -0
- package/dist/window.js.map +1 -0
- package/package.json +12 -12
- package/src/apis/browser-extension.tsx +29 -0
- package/src/apis/localstorage.test.ts +14 -6
- package/src/apis/localstorage.tsx +8 -5
- package/src/apis/oauth.tsx +5 -1
- package/src/apis/preferences.tsx +48 -22
- package/src/apis/toast.tsx +37 -62
- package/src/build.test.tsx +52 -0
- package/src/build.tsx +3 -1
- package/src/cli.tsx +99 -48
- package/src/compile.tsx +32 -11
- package/src/components/actions.tsx +94 -32
- package/src/components/detail.tsx +4 -0
- package/src/components/dropdown.tsx +47 -14
- package/src/components/extension-preferences.tsx +44 -16
- package/src/components/form/checkbox.tsx +12 -6
- package/src/components/form/date-picker.tsx +12 -6
- package/src/components/form/description.tsx +7 -2
- package/src/components/form/dropdown.tsx +131 -119
- package/src/components/form/file-autocomplete.tsx +90 -108
- package/src/components/form/file-picker.tsx +54 -43
- package/src/components/form/form-end.tsx +23 -2
- package/src/components/form/index.tsx +168 -41
- package/src/components/form/password-field.tsx +12 -6
- package/src/components/form/text-area.tsx +13 -6
- package/src/components/form/text-field.tsx +13 -6
- package/src/components/form/types.tsx +6 -0
- package/src/components/form/with-left-border.tsx +41 -8
- package/src/components/icon.tsx +27 -8
- package/src/components/list.tsx +243 -101
- package/src/components/loading-bar.tsx +3 -2
- package/src/components/loading-text.tsx +79 -0
- package/src/descendants.tsx +1 -0
- package/src/examples/file-autocomplete.vitest.tsx +130 -125
- package/src/examples/form-basic.vitest.tsx +376 -176
- package/src/examples/form-dropdown.vitest.tsx +126 -126
- package/src/examples/form-scroll.tsx +2 -0
- package/src/examples/form-scroll.vitest.tsx +58 -58
- package/src/examples/form-tagpicker.vitest.tsx +99 -99
- package/src/examples/internal/descendants-filtering.tsx +1 -0
- package/src/examples/internal/descendants.tsx +1 -0
- package/src/examples/internal/rhf-custom-ref.tsx +2 -0
- package/src/examples/internal/scrollbox-demo.tsx +3 -27
- package/src/examples/internal/scrollbox-with-descendants.tsx +4 -7
- package/src/examples/internal/simple-scrollbox.vitest.tsx +7 -5
- package/src/examples/list-detail-metadata.tsx +49 -0
- package/src/examples/list-detail-metadata.vitest.tsx +88 -0
- package/src/examples/list-dropdown-default.vitest.tsx +51 -51
- package/src/examples/list-fetch-data.vitest.tsx +4 -4
- package/src/examples/list-scrollbox.vitest.tsx +73 -14
- package/src/examples/list-with-detail-long.tsx +70 -0
- package/src/examples/list-with-detail.vitest.tsx +191 -85
- package/src/examples/list-with-dropdown.vitest.tsx +53 -53
- package/src/examples/list-with-sections.tsx +1 -0
- package/src/examples/list-with-sections.vitest.tsx +221 -97
- package/src/examples/list-with-toast.vitest.tsx +16 -16
- package/src/examples/simple-file-picker.vitest.tsx +63 -469
- package/src/examples/simple-grid.vitest.tsx +238 -233
- package/src/examples/simple-navigation.tsx +15 -7
- package/src/examples/simple-navigation.vitest.tsx +130 -219
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/swift-extension.vitest.tsx +148 -0
- package/src/examples/synonyms.vitest.tsx +159 -0
- package/src/examples/toast-variations.tsx +150 -0
- package/src/examples/toast-variations.vitest.tsx +370 -0
- package/src/extensions/dev.tsx +74 -7
- package/src/extensions/dev.vitest.tsx +102 -34
- package/src/extensions/home.tsx +6 -0
- package/src/index.tsx +3 -0
- package/src/internal/dialog.tsx +43 -30
- package/src/internal/navigation.tsx +3 -1
- package/src/internal/offscreen.tsx +15 -0
- package/src/internal/providers.tsx +4 -2
- package/src/internal/scrollbox.tsx +4 -8
- package/src/keyboard.test.tsx +69 -0
- package/src/release.tsx +32 -38
- package/src/state.tsx +7 -0
- package/src/swift-loader.tsx +239 -0
- package/src/swift-runtime.tsx +36 -0
- package/src/utils/file-system.ts +61 -0
- package/src/utils/run-command.tsx +82 -6
|
@@ -7,8 +7,10 @@ import { useFormContext, Controller } from 'react-hook-form'
|
|
|
7
7
|
import { useFocusContext, useFormFieldDescendant } from './index'
|
|
8
8
|
import { useKeyboard } from '@opentui/react'
|
|
9
9
|
import { useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
10
|
-
import {
|
|
10
|
+
import { FileAutocompleteDialog } from './file-autocomplete'
|
|
11
11
|
import { useFormNavigationHelpers } from './use-form-navigation'
|
|
12
|
+
import { useDialog } from 'termcast/src/internal/dialog'
|
|
13
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
12
14
|
|
|
13
15
|
export interface FilePickerProps extends FormItemProps<string[]> {
|
|
14
16
|
/**
|
|
@@ -52,6 +54,7 @@ const FilePickerField = ({
|
|
|
52
54
|
props,
|
|
53
55
|
isFocused,
|
|
54
56
|
setFocusedField,
|
|
57
|
+
isFormLoading,
|
|
55
58
|
}: {
|
|
56
59
|
field: any
|
|
57
60
|
fieldState: any
|
|
@@ -59,12 +62,40 @@ const FilePickerField = ({
|
|
|
59
62
|
props: FilePickerProps
|
|
60
63
|
isFocused: boolean
|
|
61
64
|
setFocusedField: (id: string) => void
|
|
65
|
+
isFormLoading: boolean
|
|
62
66
|
}): any => {
|
|
63
67
|
const isInFocus = useIsInFocus()
|
|
64
|
-
const [showAutocomplete, setShowAutocomplete] = React.useState(false)
|
|
65
|
-
const [searchTrigger, setSearchTrigger] = React.useState(0)
|
|
66
68
|
const inputRef = React.useRef<TextareaRenderable>(null)
|
|
67
|
-
const
|
|
69
|
+
const dialog = useDialog()
|
|
70
|
+
|
|
71
|
+
const showAutocomplete = () => {
|
|
72
|
+
if (dialog.stack.length > 0) return
|
|
73
|
+
|
|
74
|
+
const handleSelect = (path: string) => {
|
|
75
|
+
const currentFiles = field.value || []
|
|
76
|
+
const newFiles =
|
|
77
|
+
props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
|
|
78
|
+
field.onChange(newFiles)
|
|
79
|
+
if (props.onChange) {
|
|
80
|
+
props.onChange(newFiles)
|
|
81
|
+
}
|
|
82
|
+
inputRef.current?.setText('')
|
|
83
|
+
dialog.clear()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
dialog.push(
|
|
87
|
+
<FileAutocompleteDialog
|
|
88
|
+
onSelect={handleSelect}
|
|
89
|
+
onClose={() => {
|
|
90
|
+
dialog.clear()
|
|
91
|
+
}}
|
|
92
|
+
inputRef={inputRef}
|
|
93
|
+
canChooseFiles={props.canChooseFiles}
|
|
94
|
+
canChooseDirectories={props.canChooseDirectories}
|
|
95
|
+
initialDirectory={props.initialDirectory}
|
|
96
|
+
/>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
68
99
|
|
|
69
100
|
// Handle Enter key and left arrow for removing last file
|
|
70
101
|
useKeyboard((evt) => {
|
|
@@ -86,12 +117,11 @@ const FilePickerField = ({
|
|
|
86
117
|
if (evt.name === 'return') {
|
|
87
118
|
const inputValue = inputRef.current?.plainText || ''
|
|
88
119
|
// If input is empty, show files in current directory
|
|
89
|
-
if (!inputValue
|
|
90
|
-
|
|
91
|
-
setSearchTrigger((n) => n + 1)
|
|
120
|
+
if (!inputValue) {
|
|
121
|
+
showAutocomplete()
|
|
92
122
|
}
|
|
93
|
-
// If
|
|
94
|
-
else if (inputValue.trim()
|
|
123
|
+
// If input has value, add the path directly
|
|
124
|
+
else if (inputValue.trim()) {
|
|
95
125
|
const currentFiles = field.value || []
|
|
96
126
|
const newFiles =
|
|
97
127
|
props.allowMultipleSelection !== false
|
|
@@ -106,37 +136,30 @@ const FilePickerField = ({
|
|
|
106
136
|
}
|
|
107
137
|
})
|
|
108
138
|
|
|
109
|
-
const handleSelectFile = (path: string) => {
|
|
110
|
-
const currentFiles = field.value || []
|
|
111
|
-
const newFiles =
|
|
112
|
-
props.allowMultipleSelection !== false ? [...currentFiles, path] : [path]
|
|
113
|
-
field.onChange(newFiles)
|
|
114
|
-
if (props.onChange) {
|
|
115
|
-
props.onChange(newFiles)
|
|
116
|
-
}
|
|
117
|
-
inputRef.current?.setText('')
|
|
118
|
-
setShowAutocomplete(false)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
139
|
const selectedFiles = field.value || []
|
|
122
140
|
|
|
123
141
|
return (
|
|
124
142
|
<box flexDirection='column'>
|
|
125
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
126
|
-
<
|
|
127
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
143
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={isFormLoading}>
|
|
144
|
+
<box
|
|
128
145
|
onMouseDown={() => {
|
|
129
146
|
setFocusedField(props.id)
|
|
130
147
|
}}
|
|
131
148
|
>
|
|
132
|
-
|
|
133
|
-
|
|
149
|
+
<LoadingText
|
|
150
|
+
isLoading={isFocused && isFormLoading}
|
|
151
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
152
|
+
>
|
|
153
|
+
{props.title || 'File Path'}
|
|
154
|
+
</LoadingText>
|
|
155
|
+
</box>
|
|
134
156
|
</WithLeftBorder>
|
|
135
157
|
<WithLeftBorder isFocused={isFocused}>
|
|
136
|
-
<box flexDirection='column'
|
|
158
|
+
<box flexDirection='column'>
|
|
137
159
|
<textarea
|
|
138
160
|
ref={inputRef}
|
|
139
161
|
height={1}
|
|
162
|
+
wrapMode='none'
|
|
140
163
|
keyBindings={[
|
|
141
164
|
{ name: 'return', action: 'submit' },
|
|
142
165
|
{ name: 'linefeed', action: 'submit' },
|
|
@@ -148,10 +171,7 @@ const FilePickerField = ({
|
|
|
148
171
|
onContentChange={() => {
|
|
149
172
|
const value = inputRef.current?.plainText || ''
|
|
150
173
|
if (value && isFocused) {
|
|
151
|
-
|
|
152
|
-
setSearchTrigger((n) => n + 1)
|
|
153
|
-
} else if (!value) {
|
|
154
|
-
setShowAutocomplete(false)
|
|
174
|
+
showAutocomplete()
|
|
155
175
|
}
|
|
156
176
|
}}
|
|
157
177
|
/>
|
|
@@ -179,24 +199,14 @@ const FilePickerField = ({
|
|
|
179
199
|
<text fg={Theme.textMuted}>{props.info}</text>
|
|
180
200
|
</WithLeftBorder>
|
|
181
201
|
)}
|
|
182
|
-
<FileAutocomplete
|
|
183
|
-
onSelect={handleSelectFile}
|
|
184
|
-
visible={showAutocomplete}
|
|
185
|
-
onVisibilityChange={setShowAutocomplete}
|
|
186
|
-
inputRef={inputRef}
|
|
187
|
-
anchorRef={anchorRef}
|
|
188
|
-
searchTrigger={searchTrigger}
|
|
189
|
-
canChooseFiles={props.canChooseFiles}
|
|
190
|
-
canChooseDirectories={props.canChooseDirectories}
|
|
191
|
-
initialDirectory={props.initialDirectory}
|
|
192
|
-
/>
|
|
193
202
|
</box>
|
|
194
203
|
) as React.ReactElement
|
|
195
204
|
}
|
|
196
205
|
|
|
197
206
|
export const FilePicker = (props: FilePickerProps): any => {
|
|
198
207
|
const { control } = useFormContext()
|
|
199
|
-
const
|
|
208
|
+
const focusContext = useFocusContext()
|
|
209
|
+
const { focusedField, setFocusedField } = focusContext
|
|
200
210
|
const isFocused = focusedField === props.id
|
|
201
211
|
const isInFocus = useIsInFocus()
|
|
202
212
|
|
|
@@ -236,6 +246,7 @@ export const FilePicker = (props: FilePickerProps): any => {
|
|
|
236
246
|
props={props}
|
|
237
247
|
isFocused={isFocused}
|
|
238
248
|
setFocusedField={setFocusedField}
|
|
249
|
+
isFormLoading={focusContext.isLoading}
|
|
239
250
|
/>
|
|
240
251
|
</box>
|
|
241
252
|
) as React.ReactElement
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React, { useState, useLayoutEffect } from 'react'
|
|
2
2
|
import { Theme } from 'termcast/src/theme'
|
|
3
|
+
import { useFocusContext, useFormScrollContext } from './index'
|
|
3
4
|
|
|
4
5
|
export const FormEnd = (): any => {
|
|
5
|
-
|
|
6
|
+
const { focusedField } = useFocusContext()
|
|
7
|
+
const scrollContext = useFormScrollContext()
|
|
8
|
+
const [isLastFieldFocused, setIsLastFieldFocused] = useState(false)
|
|
9
|
+
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
if (!scrollContext || !focusedField) {
|
|
12
|
+
setIsLastFieldFocused(false)
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
const descendants = Object.values(scrollContext.descendantsContext.map.current)
|
|
16
|
+
.filter((item) => item.index !== -1 && item.props?.id)
|
|
17
|
+
.sort((a, b) => a.index - b.index)
|
|
18
|
+
if (descendants.length === 0) {
|
|
19
|
+
setIsLastFieldFocused(false)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
const lastField = descendants[descendants.length - 1]
|
|
23
|
+
setIsLastFieldFocused(lastField.props?.id === focusedField)
|
|
24
|
+
}, [focusedField])
|
|
25
|
+
|
|
26
|
+
return <text fg={isLastFieldFocused ? Theme.accent : Theme.text}>└</text>
|
|
6
27
|
}
|
|
@@ -4,6 +4,7 @@ import React, {
|
|
|
4
4
|
useContext,
|
|
5
5
|
useLayoutEffect,
|
|
6
6
|
useRef,
|
|
7
|
+
useEffect,
|
|
7
8
|
} from 'react'
|
|
8
9
|
import { useKeyboard } from '@opentui/react'
|
|
9
10
|
import { useForm, FormProvider } from 'react-hook-form'
|
|
@@ -12,6 +13,7 @@ import { logger } from 'termcast/src/logger'
|
|
|
12
13
|
import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
13
14
|
import { useDialog } from 'termcast/src/internal/dialog'
|
|
14
15
|
import { Theme } from 'termcast/src/theme'
|
|
16
|
+
import { useStore } from 'termcast/src/state'
|
|
15
17
|
import {
|
|
16
18
|
TextAttributes,
|
|
17
19
|
ScrollBoxRenderable,
|
|
@@ -33,7 +35,10 @@ import {
|
|
|
33
35
|
FormValues_2,
|
|
34
36
|
FormProps_2,
|
|
35
37
|
FormItemProps_2,
|
|
38
|
+
LinkAccessoryProps,
|
|
36
39
|
} from './types'
|
|
40
|
+
import { LoadingBar } from 'termcast/src/components/loading-bar'
|
|
41
|
+
import { useNavigationPending } from 'termcast/src/internal/navigation'
|
|
37
42
|
import { FORM_MAX_WIDTH } from './description'
|
|
38
43
|
import { ScrollBox } from 'termcast/src/internal/scrollbox'
|
|
39
44
|
|
|
@@ -67,10 +72,11 @@ export const useFormScrollContext = () => {
|
|
|
67
72
|
return useContext(FormScrollContext)
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
// Context for managing focused field
|
|
75
|
+
// Context for managing focused field and loading state
|
|
71
76
|
interface FocusContextValue {
|
|
72
77
|
focusedField: string | null
|
|
73
78
|
setFocusedField: (id: string | null) => void
|
|
79
|
+
isLoading: boolean
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
const FocusContext = createContext<FocusContextValue | null>(null)
|
|
@@ -96,9 +102,29 @@ export const useFormSubmit = () => {
|
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
function FormFooter(): any {
|
|
105
|
+
const hasToast = useStore((s) => s.toast !== null)
|
|
106
|
+
|
|
107
|
+
const content = hasToast ? null : (
|
|
108
|
+
<>
|
|
109
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
110
|
+
ctrl ↵
|
|
111
|
+
</text>
|
|
112
|
+
<text fg={Theme.textMuted}> submit</text>
|
|
113
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
114
|
+
{' '}tab
|
|
115
|
+
</text>
|
|
116
|
+
<text fg={Theme.textMuted}> navigate</text>
|
|
117
|
+
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
118
|
+
{' '}^k
|
|
119
|
+
</text>
|
|
120
|
+
<text fg={Theme.textMuted}> actions</text>
|
|
121
|
+
</>
|
|
122
|
+
)
|
|
123
|
+
|
|
99
124
|
return (
|
|
100
125
|
<box
|
|
101
126
|
border={false}
|
|
127
|
+
height={1}
|
|
102
128
|
style={{
|
|
103
129
|
paddingLeft: 1,
|
|
104
130
|
paddingRight: 1,
|
|
@@ -107,16 +133,7 @@ function FormFooter(): any {
|
|
|
107
133
|
flexDirection: 'row',
|
|
108
134
|
}}
|
|
109
135
|
>
|
|
110
|
-
|
|
111
|
-
<text fg={Theme.textMuted}> submit</text>
|
|
112
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
113
|
-
{' '}↑↓
|
|
114
|
-
</text>
|
|
115
|
-
<text fg={Theme.textMuted}> navigate</text>
|
|
116
|
-
<text fg={Theme.text} attributes={TextAttributes.BOLD}>
|
|
117
|
-
{' '}^k
|
|
118
|
-
</text>
|
|
119
|
-
<text fg={Theme.textMuted}> actions</text>
|
|
136
|
+
{content}
|
|
120
137
|
</box>
|
|
121
138
|
)
|
|
122
139
|
}
|
|
@@ -171,19 +188,30 @@ interface FormType {
|
|
|
171
188
|
FilePicker: (props: FilePickerProps) => any
|
|
172
189
|
Separator: () => any
|
|
173
190
|
Description: (props: DescriptionProps) => any
|
|
191
|
+
LinkAccessory: (props: LinkAccessoryProps) => any
|
|
174
192
|
}
|
|
175
193
|
|
|
176
194
|
export const Form: FormType = ((props) => {
|
|
195
|
+
const { navigationTitle, isLoading, searchBarAccessory } = props
|
|
177
196
|
const methods = useForm<FormValues>({
|
|
178
197
|
// defaultValues: {},
|
|
179
198
|
// mode: 'onChange',
|
|
180
199
|
})
|
|
181
200
|
|
|
182
201
|
const [focusedField, setFocusedFieldRaw] = useState<string | null>(null)
|
|
202
|
+
const navigationPending = useNavigationPending()
|
|
183
203
|
|
|
184
204
|
const scrollBoxRef = useRef<ScrollBoxRenderable>(null)
|
|
185
205
|
const descendantsContext = useFormFieldDescendants()
|
|
186
206
|
|
|
207
|
+
// Helper to get sorted field IDs
|
|
208
|
+
const getFieldIds = () => {
|
|
209
|
+
return Object.values(descendantsContext.map.current)
|
|
210
|
+
.filter((item) => item.index !== -1 && item.props?.id)
|
|
211
|
+
.sort((a, b) => a.index - b.index)
|
|
212
|
+
.map((item) => item.props!.id)
|
|
213
|
+
}
|
|
214
|
+
|
|
187
215
|
const scrollToField = (fieldId: string) => {
|
|
188
216
|
const scrollBox = scrollBoxRef.current
|
|
189
217
|
if (!scrollBox) return
|
|
@@ -197,17 +225,13 @@ export const Form: FormType = ((props) => {
|
|
|
197
225
|
|
|
198
226
|
const contentY = scrollBox.content?.y || 0
|
|
199
227
|
const viewportHeight = scrollBox.viewport?.height || 10
|
|
200
|
-
const currentScrollTop = scrollBox.scrollTop || 0
|
|
201
228
|
|
|
202
229
|
// Access current position from the BoxRenderable ref
|
|
203
230
|
const itemTop = elementRef.y - contentY
|
|
204
|
-
const itemBottom = itemTop + elementRef.height
|
|
205
231
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
scrollBox.scrollTo(itemBottom - viewportHeight)
|
|
210
|
-
}
|
|
232
|
+
// Scroll so the top of the item is centered in the viewport
|
|
233
|
+
const targetScrollTop = itemTop - viewportHeight / 2
|
|
234
|
+
scrollBox.scrollTo(Math.max(0, targetScrollTop))
|
|
211
235
|
}
|
|
212
236
|
|
|
213
237
|
const setFocusedField = (id: string | null) => {
|
|
@@ -217,30 +241,82 @@ export const Form: FormType = ((props) => {
|
|
|
217
241
|
}
|
|
218
242
|
}
|
|
219
243
|
|
|
220
|
-
//
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const firstId = descendants[0].props!.id
|
|
228
|
-
logger.log(`focusing `, firstId)
|
|
229
|
-
setFocusedFieldRaw(firstId)
|
|
230
|
-
} else {
|
|
231
|
-
logger.log(`no fields to focus in form`)
|
|
244
|
+
// Focus first field helper
|
|
245
|
+
const focusFirstField = () => {
|
|
246
|
+
const fieldIds = getFieldIds()
|
|
247
|
+
if (fieldIds.length > 0) {
|
|
248
|
+
logger.log(`focusing first field:`, fieldIds[0])
|
|
249
|
+
setFocusedField(fieldIds[0])
|
|
250
|
+
return true
|
|
232
251
|
}
|
|
233
|
-
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Focus last field helper
|
|
256
|
+
const focusLastField = () => {
|
|
257
|
+
const fieldIds = getFieldIds()
|
|
258
|
+
if (fieldIds.length > 0) {
|
|
259
|
+
logger.log(`focusing last field:`, fieldIds[fieldIds.length - 1])
|
|
260
|
+
setFocusedField(fieldIds[fieldIds.length - 1])
|
|
261
|
+
return true
|
|
262
|
+
}
|
|
263
|
+
return false
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Auto-focus first field when descendants become available
|
|
267
|
+
// Runs on every render until a field is focused (handles async loading)
|
|
268
|
+
useEffect(() => {
|
|
269
|
+
if (focusedField) return
|
|
270
|
+
|
|
271
|
+
const fieldIds = getFieldIds()
|
|
272
|
+
if (fieldIds.length > 0) {
|
|
273
|
+
logger.log(`auto-focusing first field:`, fieldIds[0])
|
|
274
|
+
setFocusedFieldRaw(fieldIds[0])
|
|
275
|
+
}
|
|
276
|
+
})
|
|
234
277
|
|
|
235
278
|
// Get focus state and dialog
|
|
236
279
|
const inFocus = useIsInFocus()
|
|
237
280
|
const dialog = useDialog()
|
|
238
281
|
|
|
239
|
-
// Handle action keys and page scrolling
|
|
282
|
+
// Handle action keys, tab navigation, and page scrolling
|
|
240
283
|
useKeyboard((evt) => {
|
|
241
284
|
// Only handle keyboard events when form is in focus
|
|
242
285
|
if (!inFocus) return
|
|
243
286
|
|
|
287
|
+
// Tab navigation at Form level - handles case when no field is focused
|
|
288
|
+
// or navigates between fields
|
|
289
|
+
if (evt.name === 'tab') {
|
|
290
|
+
if (!focusedField) {
|
|
291
|
+
// No field focused yet - focus first or last based on shift
|
|
292
|
+
if (evt.shift) {
|
|
293
|
+
focusLastField()
|
|
294
|
+
} else {
|
|
295
|
+
focusFirstField()
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
// A field is focused - navigate to next/previous
|
|
299
|
+
const fieldIds = getFieldIds()
|
|
300
|
+
const currentIndex = fieldIds.indexOf(focusedField)
|
|
301
|
+
if (evt.shift) {
|
|
302
|
+
// Shift+Tab - go to previous
|
|
303
|
+
if (currentIndex > 0) {
|
|
304
|
+
setFocusedField(fieldIds[currentIndex - 1])
|
|
305
|
+
} else {
|
|
306
|
+
setFocusedField(fieldIds[fieldIds.length - 1])
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
// Tab - go to next
|
|
310
|
+
if (currentIndex < fieldIds.length - 1) {
|
|
311
|
+
setFocusedField(fieldIds[currentIndex + 1])
|
|
312
|
+
} else {
|
|
313
|
+
setFocusedField(fieldIds[0])
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
|
|
244
320
|
// Page up/down scrolling
|
|
245
321
|
if (evt.name === 'pageup' || evt.name === 'pagedown') {
|
|
246
322
|
const scrollBox = scrollBoxRef.current
|
|
@@ -259,7 +335,7 @@ export const Form: FormType = ((props) => {
|
|
|
259
335
|
}
|
|
260
336
|
|
|
261
337
|
if (evt.name === 'k' && evt.ctrl && props.actions) {
|
|
262
|
-
// Ctrl+K shows actions
|
|
338
|
+
// Ctrl+K shows actions (always show sheet)
|
|
263
339
|
dialog.push(
|
|
264
340
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
265
341
|
{props.actions}
|
|
@@ -267,7 +343,8 @@ export const Form: FormType = ((props) => {
|
|
|
267
343
|
'bottom-right',
|
|
268
344
|
)
|
|
269
345
|
} else if (evt.name === 'return' && evt.ctrl && props.actions) {
|
|
270
|
-
// Ctrl+Return
|
|
346
|
+
// Ctrl+Return executes first action directly
|
|
347
|
+
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
271
348
|
dialog.push(
|
|
272
349
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
273
350
|
{props.actions}
|
|
@@ -275,7 +352,8 @@ export const Form: FormType = ((props) => {
|
|
|
275
352
|
'bottom-right',
|
|
276
353
|
)
|
|
277
354
|
} else if (evt.name === 'return' && evt.meta && props.actions) {
|
|
278
|
-
// Cmd+Return also
|
|
355
|
+
// Cmd+Return also executes first action directly
|
|
356
|
+
useStore.setState({ shouldAutoExecuteFirstAction: true })
|
|
279
357
|
dialog.push(
|
|
280
358
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
281
359
|
{props.actions}
|
|
@@ -298,9 +376,9 @@ export const Form: FormType = ((props) => {
|
|
|
298
376
|
<FormProvider {...methods}>
|
|
299
377
|
<FormSubmitContext.Provider value={submitContextValue}>
|
|
300
378
|
<FormScrollContext.Provider value={scrollContextValue}>
|
|
301
|
-
<FocusContext.Provider value={{ focusedField, setFocusedField }}>
|
|
379
|
+
<FocusContext.Provider value={{ focusedField, setFocusedField, isLoading: isLoading || false }}>
|
|
302
380
|
<box flexDirection='row' flexGrow={1} justifyContent='center'>
|
|
303
|
-
<box flexDirection='column'>
|
|
381
|
+
<box flexGrow={0} flexDirection='column'>
|
|
304
382
|
<ScrollBox
|
|
305
383
|
ref={scrollBoxRef}
|
|
306
384
|
flexGrow={1}
|
|
@@ -308,15 +386,40 @@ export const Form: FormType = ((props) => {
|
|
|
308
386
|
rootOptions: {
|
|
309
387
|
maxWidth: FORM_MAX_WIDTH,
|
|
310
388
|
},
|
|
389
|
+
|
|
311
390
|
contentOptions: {
|
|
312
391
|
justifyContent: 'center',
|
|
313
392
|
},
|
|
314
393
|
}}
|
|
315
394
|
>
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
395
|
+
{/* Navigation header with title, loading bar, and accessory */}
|
|
396
|
+
|
|
397
|
+
{/*<box
|
|
398
|
+
border={false}
|
|
399
|
+
style={{
|
|
400
|
+
flexShrink: 0,
|
|
401
|
+
flexDirection: 'row',
|
|
402
|
+
paddingBottom: 1,
|
|
403
|
+
justifyContent: 'space-between',
|
|
404
|
+
alignItems: 'center',
|
|
405
|
+
gap: 3,
|
|
406
|
+
}}
|
|
407
|
+
>
|
|
408
|
+
<box overflow='hidden'>
|
|
409
|
+
<LoadingBar
|
|
410
|
+
title={navigationTitle || ''}
|
|
411
|
+
isLoading={isLoading || navigationPending}
|
|
412
|
+
/>
|
|
413
|
+
</box>
|
|
414
|
+
{searchBarAccessory}
|
|
415
|
+
</box>*/}
|
|
416
|
+
|
|
417
|
+
<box>
|
|
418
|
+
<FormFieldDescendantsProvider value={descendantsContext}>
|
|
419
|
+
{props.children}
|
|
420
|
+
<FormEnd />
|
|
421
|
+
</FormFieldDescendantsProvider>
|
|
422
|
+
</box>
|
|
320
423
|
</ScrollBox>
|
|
321
424
|
<FormFooter />
|
|
322
425
|
</box>
|
|
@@ -351,3 +454,27 @@ Form.TagPicker = TagPicker
|
|
|
351
454
|
Form.FilePicker = FilePicker
|
|
352
455
|
Form.Separator = Separator
|
|
353
456
|
Form.Description = Description
|
|
457
|
+
|
|
458
|
+
// LinkAccessory component - shows a link in the navigation bar
|
|
459
|
+
function LinkAccessory(props: LinkAccessoryProps): any {
|
|
460
|
+
return (
|
|
461
|
+
<box
|
|
462
|
+
style={{
|
|
463
|
+
flexShrink: 0,
|
|
464
|
+
maxWidth: '50%',
|
|
465
|
+
overflow: 'hidden',
|
|
466
|
+
paddingRight: 1,
|
|
467
|
+
}}
|
|
468
|
+
>
|
|
469
|
+
<text
|
|
470
|
+
fg={Theme.textMuted}
|
|
471
|
+
attributes={TextAttributes.UNDERLINE}
|
|
472
|
+
wrapMode='none'
|
|
473
|
+
>
|
|
474
|
+
{props.text}
|
|
475
|
+
</text>
|
|
476
|
+
</box>
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
Form.LinkAccessory = LinkAccessory
|
|
@@ -6,6 +6,7 @@ import { FormItemProps, FormItemRef } from './types'
|
|
|
6
6
|
import { Theme } from 'termcast/src/theme'
|
|
7
7
|
import { WithLeftBorder } from './with-left-border'
|
|
8
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
9
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
9
10
|
|
|
10
11
|
export interface PasswordFieldProps extends FormItemProps<string> {
|
|
11
12
|
placeholder?: string
|
|
@@ -15,7 +16,8 @@ export type PasswordFieldRef = FormItemRef
|
|
|
15
16
|
|
|
16
17
|
export const PasswordField = (props: PasswordFieldProps): any => {
|
|
17
18
|
const { control } = useFormContext()
|
|
18
|
-
const
|
|
19
|
+
const focusContext = useFocusContext()
|
|
20
|
+
const { focusedField, setFocusedField } = focusContext
|
|
19
21
|
const isFocused = focusedField === props.id
|
|
20
22
|
const elementRef = useRef<BoxRenderable>(null)
|
|
21
23
|
const realValueRef = useRef(props.defaultValue || props.value || '')
|
|
@@ -38,15 +40,19 @@ export const PasswordField = (props: PasswordFieldProps): any => {
|
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<box ref={elementRef} flexDirection="column">
|
|
41
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
42
|
-
<
|
|
43
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
43
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
|
|
44
|
+
<box
|
|
44
45
|
onMouseDown={() => {
|
|
45
46
|
setFocusedField(props.id)
|
|
46
47
|
}}
|
|
47
48
|
>
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
<LoadingText
|
|
50
|
+
isLoading={isFocused && focusContext.isLoading}
|
|
51
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
52
|
+
>
|
|
53
|
+
{props.title || ''}
|
|
54
|
+
</LoadingText>
|
|
55
|
+
</box>
|
|
50
56
|
</WithLeftBorder>
|
|
51
57
|
<WithLeftBorder isFocused={isFocused}>
|
|
52
58
|
<input
|
|
@@ -7,6 +7,7 @@ import { Theme } from 'termcast/src/theme'
|
|
|
7
7
|
import { WithLeftBorder } from './with-left-border'
|
|
8
8
|
import { useFormNavigation } from './use-form-navigation'
|
|
9
9
|
import { createTextareaFormRef } from './form-ref'
|
|
10
|
+
import { LoadingText } from 'termcast/src/components/loading-text'
|
|
10
11
|
|
|
11
12
|
export interface TextAreaProps extends FormItemProps<string> {
|
|
12
13
|
placeholder?: string
|
|
@@ -17,7 +18,8 @@ export type TextAreaRef = FormItemRef
|
|
|
17
18
|
|
|
18
19
|
export const TextArea = (props: TextAreaProps): any => {
|
|
19
20
|
const { register, formState } = useFormContext()
|
|
20
|
-
const
|
|
21
|
+
const focusContext = useFocusContext()
|
|
22
|
+
const { focusedField, setFocusedField } = focusContext
|
|
21
23
|
const isFocused = focusedField === props.id
|
|
22
24
|
|
|
23
25
|
const elementRef = useRef<BoxRenderable>(null)
|
|
@@ -58,21 +60,26 @@ export const TextArea = (props: TextAreaProps): any => {
|
|
|
58
60
|
|
|
59
61
|
return (
|
|
60
62
|
<box ref={elementRef} flexDirection="column">
|
|
61
|
-
<WithLeftBorder withDiamond isFocused={isFocused}>
|
|
62
|
-
<
|
|
63
|
-
fg={isFocused ? Theme.primary : Theme.text}
|
|
63
|
+
<WithLeftBorder withDiamond isFocused={isFocused} isLoading={focusContext.isLoading}>
|
|
64
|
+
<box
|
|
64
65
|
onMouseDown={() => {
|
|
65
66
|
setFocusedField(props.id)
|
|
66
67
|
}}
|
|
67
68
|
>
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
<LoadingText
|
|
70
|
+
isLoading={isFocused && focusContext.isLoading}
|
|
71
|
+
color={isFocused ? Theme.primary : Theme.text}
|
|
72
|
+
>
|
|
73
|
+
{props.title || ''}
|
|
74
|
+
</LoadingText>
|
|
75
|
+
</box>
|
|
70
76
|
</WithLeftBorder>
|
|
71
77
|
|
|
72
78
|
<WithLeftBorder isFocused={isFocused}>
|
|
73
79
|
<box flexGrow={1}>
|
|
74
80
|
<textarea
|
|
75
81
|
ref={handleRef}
|
|
82
|
+
wrapMode='none'
|
|
76
83
|
initialValue={props.defaultValue || props.value || ''}
|
|
77
84
|
onContentChange={handleContentChange}
|
|
78
85
|
minHeight={4}
|