termcast 1.3.21 → 1.3.24
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/build.d.ts.map +1 -1
- package/dist/build.js +2 -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 +67 -31
- 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.map +1 -1
- package/dist/compile.js +22 -5
- 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 +100 -28
- 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 +140 -62
- 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/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/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 +25 -1
- package/dist/utils/run-command.d.ts.map +1 -1
- package/dist/utils/run-command.js +47 -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 -10
- 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/build.test.tsx +52 -0
- package/src/build.tsx +2 -1
- package/src/cli.tsx +78 -37
- package/src/compile.tsx +22 -5
- 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 +152 -34
- 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 +193 -74
- 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 +198 -92
- 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 +213 -89
- package/src/examples/list-with-toast.vitest.tsx +4 -4
- package/src/examples/simple-file-picker.vitest.tsx +61 -471
- package/src/examples/simple-grid.vitest.tsx +238 -233
- package/src/examples/simple-navigation.tsx +15 -7
- package/src/examples/simple-navigation.vitest.tsx +121 -210
- 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/extensions/dev.tsx +74 -7
- package/src/extensions/dev.vitest.tsx +97 -31
- 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/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 +75 -6
package/src/extensions/home.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
import React from 'react'
|
|
3
4
|
import { createRoot } from '@opentui/react'
|
|
4
5
|
import { createCliRenderer } from '@opentui/core'
|
|
@@ -17,6 +18,7 @@ import '../globals'
|
|
|
17
18
|
interface ExtensionCommand {
|
|
18
19
|
extensionName: string
|
|
19
20
|
extensionTitle: string
|
|
21
|
+
extensionDir?: string
|
|
20
22
|
command: any
|
|
21
23
|
bundledPath?: string
|
|
22
24
|
Component?: () => any
|
|
@@ -52,6 +54,8 @@ function ExtensionsList({
|
|
|
52
54
|
const handleCommandSelect = async (item: ExtensionCommand) => {
|
|
53
55
|
clearCommandArguments()
|
|
54
56
|
|
|
57
|
+
|
|
58
|
+
|
|
55
59
|
try {
|
|
56
60
|
await runCommand({
|
|
57
61
|
command: item.command,
|
|
@@ -208,12 +212,14 @@ export default function Home({
|
|
|
208
212
|
const packageJson = JSON.parse(
|
|
209
213
|
fs.readFileSync(extension.packageJsonPath, 'utf-8'),
|
|
210
214
|
)
|
|
215
|
+
const extensionPath = path.dirname(extension.packageJsonPath)
|
|
211
216
|
|
|
212
217
|
for (const command of extension.commands) {
|
|
213
218
|
if (command.bundledPath) {
|
|
214
219
|
allCommands.push({
|
|
215
220
|
extensionName: extension.name,
|
|
216
221
|
extensionTitle: packageJson.title || extension.name,
|
|
222
|
+
extensionDir: extensionPath,
|
|
217
223
|
command,
|
|
218
224
|
bundledPath: command.bundledPath,
|
|
219
225
|
packageJson,
|
package/src/index.tsx
CHANGED
package/src/internal/dialog.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useKeyboard, useTerminalDimensions } from '@opentui/react'
|
|
2
|
-
import React, { type ReactNode, useRef } from 'react'
|
|
2
|
+
import React, { type ReactNode, useRef, useContext } from 'react'
|
|
3
3
|
import { Theme } from 'termcast/src/theme'
|
|
4
4
|
import { InFocus, useIsInFocus } from 'termcast/src/internal/focus-context'
|
|
5
5
|
import { CommonProps } from 'termcast/src/utils'
|
|
@@ -10,13 +10,14 @@ import {
|
|
|
10
10
|
} from 'termcast/src/state'
|
|
11
11
|
import { logger } from '../logger'
|
|
12
12
|
import { ToastOverlay } from 'termcast/src/apis/toast'
|
|
13
|
+
import { NavigationContext } from 'termcast/src/internal/navigation'
|
|
13
14
|
|
|
14
15
|
const Border = {
|
|
15
|
-
topLeft: '',
|
|
16
|
-
topRight: '',
|
|
16
|
+
topLeft: '┏',
|
|
17
|
+
topRight: '┓',
|
|
17
18
|
bottomLeft: '',
|
|
18
19
|
bottomRight: '',
|
|
19
|
-
horizontal: '',
|
|
20
|
+
horizontal: '━',
|
|
20
21
|
vertical: '┃',
|
|
21
22
|
topT: '+',
|
|
22
23
|
bottomT: '+',
|
|
@@ -107,7 +108,7 @@ export function Dialog({
|
|
|
107
108
|
onMouseDown={handleBackdropClick}
|
|
108
109
|
>
|
|
109
110
|
<box
|
|
110
|
-
border
|
|
111
|
+
border
|
|
111
112
|
customBorderChars={Border}
|
|
112
113
|
width={76}
|
|
113
114
|
maxWidth={dimensions.width - 2}
|
|
@@ -145,31 +146,6 @@ export function DialogProvider(props: DialogProviderProps): any {
|
|
|
145
146
|
return (
|
|
146
147
|
<>
|
|
147
148
|
<InFocus inFocus={!dialogStack?.length}>{props.children}</InFocus>
|
|
148
|
-
{dialogStack.length > 0 && (
|
|
149
|
-
<box position='absolute'>
|
|
150
|
-
{dialogStack.map((item, index) => {
|
|
151
|
-
const isLastItem = index === dialogStack.length - 1
|
|
152
|
-
return (
|
|
153
|
-
<InFocus key={'dialog' + String(index)} inFocus={isLastItem}>
|
|
154
|
-
<Dialog
|
|
155
|
-
position={item.position}
|
|
156
|
-
onClickOutside={() => {
|
|
157
|
-
if (!isLastItem) return
|
|
158
|
-
const state = useStore.getState()
|
|
159
|
-
if (state.dialogStack.length > 0) {
|
|
160
|
-
useStore.setState({
|
|
161
|
-
dialogStack: state.dialogStack.slice(0, -1),
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
{item.element}
|
|
167
|
-
</Dialog>
|
|
168
|
-
</InFocus>
|
|
169
|
-
)
|
|
170
|
-
})}
|
|
171
|
-
</box>
|
|
172
|
-
)}
|
|
173
149
|
<InFocus inFocus={false}>
|
|
174
150
|
<ToastOverlay />
|
|
175
151
|
</InFocus>
|
|
@@ -177,6 +153,43 @@ export function DialogProvider(props: DialogProviderProps): any {
|
|
|
177
153
|
)
|
|
178
154
|
}
|
|
179
155
|
|
|
156
|
+
export function DialogOverlay(): any {
|
|
157
|
+
const dialogStack = useStore((state) => state.dialogStack)
|
|
158
|
+
const navContext = useContext(NavigationContext)
|
|
159
|
+
|
|
160
|
+
if (dialogStack.length === 0) {
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<box position='absolute'>
|
|
166
|
+
{dialogStack.map((item, index) => {
|
|
167
|
+
const isLastItem = index === dialogStack.length - 1
|
|
168
|
+
return (
|
|
169
|
+
<InFocus key={'dialog' + String(index)} inFocus={isLastItem}>
|
|
170
|
+
<Dialog
|
|
171
|
+
position={item.position}
|
|
172
|
+
onClickOutside={() => {
|
|
173
|
+
if (!isLastItem) return
|
|
174
|
+
const state = useStore.getState()
|
|
175
|
+
if (state.dialogStack.length > 0) {
|
|
176
|
+
useStore.setState({
|
|
177
|
+
dialogStack: state.dialogStack.slice(0, -1),
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<NavigationContext.Provider value={navContext}>
|
|
183
|
+
{item.element}
|
|
184
|
+
</NavigationContext.Provider>
|
|
185
|
+
</Dialog>
|
|
186
|
+
</InFocus>
|
|
187
|
+
)
|
|
188
|
+
})}
|
|
189
|
+
</box>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
180
193
|
export function useDialog() {
|
|
181
194
|
const dialogStack = useStore((state) => state.dialogStack)
|
|
182
195
|
|
|
@@ -26,12 +26,13 @@ interface NavigationContextType {
|
|
|
26
26
|
isPending: boolean
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
const NavigationContext = createContext<NavigationContextType | undefined>(
|
|
29
|
+
export const NavigationContext = createContext<NavigationContextType | undefined>(
|
|
30
30
|
undefined,
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
interface NavigationProviderProps extends CommonProps {
|
|
34
34
|
children: ReactNode
|
|
35
|
+
overlay?: ReactNode
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export function NavigationProvider(props: NavigationProviderProps): any {
|
|
@@ -139,6 +140,7 @@ export function NavigationProvider(props: NavigationProviderProps): any {
|
|
|
139
140
|
{React.cloneElement(currentItem?.element as React.ReactElement, {
|
|
140
141
|
key: stack.length,
|
|
141
142
|
})}
|
|
143
|
+
{props.overlay}
|
|
142
144
|
</NavigationContext.Provider>
|
|
143
145
|
)
|
|
144
146
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createContext, useContext, ReactNode } from 'react'
|
|
2
|
+
|
|
3
|
+
const OffscreenContext = createContext(false)
|
|
4
|
+
|
|
5
|
+
export function useIsOffscreen(): boolean {
|
|
6
|
+
return useContext(OffscreenContext)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Offscreen({ children }: { children: ReactNode }): any {
|
|
10
|
+
return (
|
|
11
|
+
<OffscreenContext.Provider value={true}>
|
|
12
|
+
{children}
|
|
13
|
+
</OffscreenContext.Provider>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -6,7 +6,7 @@ import React, {
|
|
|
6
6
|
} from 'react'
|
|
7
7
|
import { QueryClient } from '@tanstack/react-query'
|
|
8
8
|
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
|
|
9
|
-
import { DialogProvider } from 'termcast/src/internal/dialog'
|
|
9
|
+
import { DialogProvider, DialogOverlay } from 'termcast/src/internal/dialog'
|
|
10
10
|
import { NavigationProvider } from 'termcast/src/internal/navigation'
|
|
11
11
|
import { CommonProps } from 'termcast/src/utils'
|
|
12
12
|
import { Cache } from 'termcast/src/apis/cache'
|
|
@@ -420,7 +420,9 @@ export function TermcastProvider(props: ProvidersProps): any {
|
|
|
420
420
|
<DialogProvider>
|
|
421
421
|
<box padding={2}>
|
|
422
422
|
{/* NavigationProvider must be last to ensure parent providers remain in the tree when navigation changes */}
|
|
423
|
-
<NavigationProvider
|
|
423
|
+
<NavigationProvider overlay={<DialogOverlay />}>
|
|
424
|
+
{props.children}
|
|
425
|
+
</NavigationProvider>
|
|
424
426
|
</box>
|
|
425
427
|
</DialogProvider>
|
|
426
428
|
</PersistQueryClientProvider>
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import Theme from '../theme'
|
|
3
|
+
import { MacOSScrollAccel } from '@opentui/core'
|
|
4
|
+
import { ScrollBoxProps } from '@opentui/react'
|
|
3
5
|
|
|
4
|
-
interface ScrollBoxProps {
|
|
5
|
-
children?: React.ReactNode
|
|
6
|
-
focused?: boolean
|
|
7
|
-
flexGrow?: number
|
|
8
|
-
flexShrink?: number
|
|
9
|
-
style?: any
|
|
10
|
-
ref?: React.Ref<any>
|
|
11
|
-
}
|
|
12
6
|
|
|
13
7
|
export function ScrollBox({
|
|
14
8
|
children,
|
|
@@ -23,6 +17,7 @@ export function ScrollBox({
|
|
|
23
17
|
<scrollbox
|
|
24
18
|
ref={ref}
|
|
25
19
|
focused={focused}
|
|
20
|
+
scrollAcceleration={new MacOSScrollAccel()}
|
|
26
21
|
flexGrow={flexGrow}
|
|
27
22
|
flexShrink={flexShrink}
|
|
28
23
|
style={{
|
|
@@ -34,6 +29,7 @@ export function ScrollBox({
|
|
|
34
29
|
flexGrow: 1,
|
|
35
30
|
flexShrink: 1,
|
|
36
31
|
paddingRight: 1,
|
|
32
|
+
|
|
37
33
|
...(style?.viewportOptions || {}),
|
|
38
34
|
},
|
|
39
35
|
contentOptions: {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import { Keyboard } from 'termcast/src/keyboard'
|
|
3
|
+
import type {
|
|
4
|
+
KeyboardKeyEquivalent,
|
|
5
|
+
KeyboardKeyModifier,
|
|
6
|
+
KeyboardShortcut,
|
|
7
|
+
} from 'termcast/src/keyboard'
|
|
8
|
+
|
|
9
|
+
describe('Keyboard', () => {
|
|
10
|
+
test('Keyboard.Shortcut.Common has all expected shortcuts', () => {
|
|
11
|
+
const commonShortcuts = Object.keys(Keyboard.Shortcut.Common)
|
|
12
|
+
expect(commonShortcuts).toMatchInlineSnapshot(`
|
|
13
|
+
[
|
|
14
|
+
"Copy",
|
|
15
|
+
"CopyDeeplink",
|
|
16
|
+
"CopyName",
|
|
17
|
+
"CopyPath",
|
|
18
|
+
"Save",
|
|
19
|
+
"Duplicate",
|
|
20
|
+
"Edit",
|
|
21
|
+
"MoveDown",
|
|
22
|
+
"MoveUp",
|
|
23
|
+
"New",
|
|
24
|
+
"Open",
|
|
25
|
+
"OpenWith",
|
|
26
|
+
"Pin",
|
|
27
|
+
"Refresh",
|
|
28
|
+
"Remove",
|
|
29
|
+
"RemoveAll",
|
|
30
|
+
"ToggleQuickLook",
|
|
31
|
+
]
|
|
32
|
+
`)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('Keyboard.Shortcut.Common.Open has correct structure', () => {
|
|
36
|
+
expect(Keyboard.Shortcut.Common.Open).toMatchInlineSnapshot(`
|
|
37
|
+
{
|
|
38
|
+
"key": "o",
|
|
39
|
+
"modifiers": [
|
|
40
|
+
"cmd",
|
|
41
|
+
],
|
|
42
|
+
}
|
|
43
|
+
`)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('Keyboard.Shortcut.Common.Copy has correct structure', () => {
|
|
47
|
+
expect(Keyboard.Shortcut.Common.Copy).toMatchInlineSnapshot(`
|
|
48
|
+
{
|
|
49
|
+
"key": "c",
|
|
50
|
+
"modifiers": [
|
|
51
|
+
"cmd",
|
|
52
|
+
"shift",
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
`)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('Keyboard.Shortcut.Common.Remove uses ctrl modifier', () => {
|
|
59
|
+
expect(Keyboard.Shortcut.Common.Remove).toMatchInlineSnapshot(`
|
|
60
|
+
{
|
|
61
|
+
"key": "x",
|
|
62
|
+
"modifiers": [
|
|
63
|
+
"ctrl",
|
|
64
|
+
],
|
|
65
|
+
}
|
|
66
|
+
`)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
})
|
package/src/state.tsx
CHANGED
|
@@ -30,6 +30,10 @@ interface AppState {
|
|
|
30
30
|
// OAuth state
|
|
31
31
|
googleAccessToken?: string
|
|
32
32
|
googleIdToken?: string
|
|
33
|
+
// Actions state - when true, auto-execute first action instead of showing sheet
|
|
34
|
+
shouldAutoExecuteFirstAction: boolean
|
|
35
|
+
// First action title for footer display (set by offscreen ActionPanel)
|
|
36
|
+
firstActionTitle: string
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
export const useStore = create<AppState>(() => ({
|
|
@@ -48,4 +52,7 @@ export const useStore = create<AppState>(() => ({
|
|
|
48
52
|
// OAuth state
|
|
49
53
|
googleAccessToken: undefined,
|
|
50
54
|
googleIdToken: undefined,
|
|
55
|
+
// Actions state
|
|
56
|
+
shouldAutoExecuteFirstAction: false,
|
|
57
|
+
firstActionTitle: '',
|
|
51
58
|
}))
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
4
|
+
import type { BunPlugin } from 'bun'
|
|
5
|
+
import { logger } from './logger'
|
|
6
|
+
|
|
7
|
+
const SWIFT_NAMESPACE = 'swift-loader'
|
|
8
|
+
|
|
9
|
+
interface SwiftBuildCache {
|
|
10
|
+
[swiftPath: string]: {
|
|
11
|
+
result: SwiftBuildResult
|
|
12
|
+
buildTime: number
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const buildCache: SwiftBuildCache = {}
|
|
17
|
+
|
|
18
|
+
async function buildSwiftPackage(swiftPath: string): Promise<SwiftBuildResult> {
|
|
19
|
+
logger.log(`Swift: building package at ${swiftPath}`)
|
|
20
|
+
|
|
21
|
+
// Check if already built and cached in memory
|
|
22
|
+
const cached = buildCache[swiftPath]
|
|
23
|
+
if (cached && fs.existsSync(cached.result.binaryPath)) {
|
|
24
|
+
logger.log(`Swift: using cached build at ${cached.result.binaryPath}`)
|
|
25
|
+
return cached.result
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check if binary already exists on disk (from previous build/process)
|
|
29
|
+
try {
|
|
30
|
+
const existingResult = await findSwiftBuildResult(swiftPath)
|
|
31
|
+
logger.log(`Swift: found existing binary at ${existingResult.binaryPath}`)
|
|
32
|
+
buildCache[swiftPath] = { result: existingResult, buildTime: Date.now() }
|
|
33
|
+
return existingResult
|
|
34
|
+
} catch {
|
|
35
|
+
// Binary doesn't exist, need to build
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Run swift build using Node's spawn
|
|
39
|
+
// Use debug build for faster compilation (skips optimizations)
|
|
40
|
+
logger.log(`Swift: running swift build -c debug in ${swiftPath}`)
|
|
41
|
+
const exitCode = await new Promise<number>((resolve, reject) => {
|
|
42
|
+
const proc = spawn('swift', ['build', '-c', 'debug'], {
|
|
43
|
+
cwd: swiftPath,
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
})
|
|
46
|
+
proc.on('error', reject)
|
|
47
|
+
proc.on('close', (code) => resolve(code ?? 1))
|
|
48
|
+
})
|
|
49
|
+
logger.log(`Swift: build exited with code ${exitCode}`)
|
|
50
|
+
|
|
51
|
+
if (exitCode !== 0) {
|
|
52
|
+
throw new Error(`Swift build failed with exit code ${exitCode}`)
|
|
53
|
+
}
|
|
54
|
+
logger.log(`Swift: build completed successfully`)
|
|
55
|
+
|
|
56
|
+
// Find the binary and generated files
|
|
57
|
+
const result = await findSwiftBuildResult(swiftPath)
|
|
58
|
+
|
|
59
|
+
// Cache the result
|
|
60
|
+
buildCache[swiftPath] = {
|
|
61
|
+
result,
|
|
62
|
+
buildTime: Date.now(),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
logger.log(`Swift: built binary at ${result.binaryPath}`)
|
|
66
|
+
return result
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface SwiftBuildResult {
|
|
70
|
+
binaryPath: string
|
|
71
|
+
raycastJsPath: string
|
|
72
|
+
packageName: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function findFilesRecursively(dir: string, filename: string): string[] {
|
|
76
|
+
const results: string[] = []
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(dir)) {
|
|
79
|
+
return results
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const fullPath = path.join(dir, entry.name)
|
|
85
|
+
if (entry.isDirectory()) {
|
|
86
|
+
results.push(...findFilesRecursively(fullPath, filename))
|
|
87
|
+
} else if (entry.name === filename) {
|
|
88
|
+
results.push(fullPath)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return results
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function findSwiftBuildResult(swiftPath: string): Promise<SwiftBuildResult> {
|
|
95
|
+
// Parse Package.swift to find executable target name
|
|
96
|
+
const packageSwiftPath = path.join(swiftPath, 'Package.swift')
|
|
97
|
+
const packageSwift = fs.readFileSync(packageSwiftPath, 'utf-8')
|
|
98
|
+
|
|
99
|
+
// Look for: name: "PackageName" at the top level
|
|
100
|
+
const nameMatch = packageSwift.match(/name:\s*"([^"]+)"/)
|
|
101
|
+
if (!nameMatch) {
|
|
102
|
+
throw new Error(`Could not find package name in ${packageSwiftPath}`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const packageName = nameMatch[1]
|
|
106
|
+
|
|
107
|
+
// Find binary - check debug first, then release
|
|
108
|
+
const debugBinaryPath = path.join(swiftPath, '.build', 'debug', packageName)
|
|
109
|
+
const releaseBinaryPath = path.join(swiftPath, '.build', 'release', packageName)
|
|
110
|
+
|
|
111
|
+
let binaryPath: string
|
|
112
|
+
if (fs.existsSync(debugBinaryPath)) {
|
|
113
|
+
binaryPath = debugBinaryPath
|
|
114
|
+
} else if (fs.existsSync(releaseBinaryPath)) {
|
|
115
|
+
binaryPath = releaseBinaryPath
|
|
116
|
+
} else {
|
|
117
|
+
throw new Error(`Swift binary not found at ${debugBinaryPath} or ${releaseBinaryPath}`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Find raycast.js using glob-like search
|
|
121
|
+
const buildDir = path.join(swiftPath, '.build')
|
|
122
|
+
const raycastJsFiles = findFilesRecursively(buildDir, 'raycast.js')
|
|
123
|
+
|
|
124
|
+
// Filter to find the one from RaycastTypeScriptPlugin for our package
|
|
125
|
+
const matchingFiles = raycastJsFiles.filter(f =>
|
|
126
|
+
f.includes('RaycastTypeScriptPlugin') && f.includes(packageName)
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if (matchingFiles.length === 0) {
|
|
130
|
+
throw new Error(`Generated raycast.js not found in ${buildDir}. Found files: ${raycastJsFiles.join(', ') || 'none'}`)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Prefer the one in plugins/outputs (not index-build)
|
|
134
|
+
const raycastJsPath = matchingFiles.find(f => f.includes('plugins/outputs')) || matchingFiles[0]
|
|
135
|
+
|
|
136
|
+
logger.log(`Swift: found raycast.js at ${raycastJsPath}`)
|
|
137
|
+
return { binaryPath, raycastJsPath, packageName }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function generateSwiftModule(result: SwiftBuildResult): string {
|
|
141
|
+
// Read the generated raycast.js from RaycastTypeScriptPlugin
|
|
142
|
+
// This file exports named functions that call runSwiftFunction(functionName, ...args)
|
|
143
|
+
// We prepend our runSwiftFunction implementation with the binary path
|
|
144
|
+
const generatedJs = fs.readFileSync(result.raycastJsPath, 'utf-8')
|
|
145
|
+
|
|
146
|
+
// Prepend our runSwiftFunction implementation using Node's child_process
|
|
147
|
+
// Use ESM import to match the export statements in generated raycast.js
|
|
148
|
+
const runtime = `
|
|
149
|
+
import { spawn as _spawn } from 'node:child_process';
|
|
150
|
+
|
|
151
|
+
const BINARY_PATH = ${JSON.stringify(result.binaryPath)};
|
|
152
|
+
|
|
153
|
+
function runSwiftFunction(functionName, ...args) {
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
const jsonArgs = args.map((arg) => JSON.stringify(arg));
|
|
156
|
+
const proc = _spawn(BINARY_PATH, [functionName, ...jsonArgs], {
|
|
157
|
+
stdio: ['pipe', 'pipe', 'inherit'],
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let stdout = '';
|
|
161
|
+
proc.stdout.on('data', (data) => {
|
|
162
|
+
stdout += data.toString();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
proc.on('error', (err) => {
|
|
166
|
+
reject(new Error('Swift function "' + functionName + '" failed to spawn: ' + err.message));
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
proc.on('close', (exitCode) => {
|
|
170
|
+
if (exitCode !== 0) {
|
|
171
|
+
reject(new Error('Swift function "' + functionName + '" failed with exit code ' + exitCode));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const trimmed = stdout.trim();
|
|
176
|
+
if (!trimmed) {
|
|
177
|
+
resolve(undefined);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
resolve(JSON.parse(trimmed));
|
|
183
|
+
} catch {
|
|
184
|
+
reject(new Error('Swift function "' + functionName + '" returned invalid JSON: ' + trimmed));
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
`
|
|
191
|
+
|
|
192
|
+
return runtime + generatedJs
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const swiftLoaderPlugin: BunPlugin = {
|
|
196
|
+
name: 'swift-loader',
|
|
197
|
+
async setup(build) {
|
|
198
|
+
// Resolve swift: imports
|
|
199
|
+
build.onResolve({ filter: /^swift:/ }, (args) => {
|
|
200
|
+
logger.log(`Swift: onResolve called for ${args.path} from ${args.importer}`)
|
|
201
|
+
|
|
202
|
+
// Extract the path after "swift:"
|
|
203
|
+
const swiftRelativePath = args.path.slice(6) // Remove "swift:"
|
|
204
|
+
|
|
205
|
+
// Resolve relative to the importer's directory
|
|
206
|
+
const importerDir = args.importer ? path.dirname(args.importer) : process.cwd()
|
|
207
|
+
const resolvedSwiftPath = path.resolve(importerDir, swiftRelativePath)
|
|
208
|
+
|
|
209
|
+
logger.log(`Swift: resolved to ${resolvedSwiftPath}`)
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
path: resolvedSwiftPath,
|
|
213
|
+
namespace: SWIFT_NAMESPACE,
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// Load swift packages
|
|
218
|
+
build.onLoad({ filter: /.*/, namespace: SWIFT_NAMESPACE }, async (args) => {
|
|
219
|
+
logger.log(`Swift: onLoad called for ${args.path}`)
|
|
220
|
+
const swiftPath = args.path
|
|
221
|
+
|
|
222
|
+
// Build the Swift package and get paths to binary + generated JS
|
|
223
|
+
logger.log(`Swift: calling buildSwiftPackage...`)
|
|
224
|
+
const result = await buildSwiftPackage(swiftPath)
|
|
225
|
+
logger.log(`Swift: buildSwiftPackage returned, binaryPath=${result.binaryPath}`)
|
|
226
|
+
|
|
227
|
+
// Generate module by combining runtime with generated raycast.js
|
|
228
|
+
logger.log(`Swift: generating module...`)
|
|
229
|
+
const contents = generateSwiftModule(result)
|
|
230
|
+
logger.log(`Swift: module generated, length=${contents.length}`)
|
|
231
|
+
// logger.log(`Swift: module contents:\n${contents}`)
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
contents,
|
|
235
|
+
loader: 'js',
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
},
|
|
239
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { logger } from './logger'
|
|
2
|
+
|
|
3
|
+
export async function runSwiftFunction(
|
|
4
|
+
binaryPath: string,
|
|
5
|
+
functionName: string,
|
|
6
|
+
...args: unknown[]
|
|
7
|
+
): Promise<unknown> {
|
|
8
|
+
const jsonArgs = args.map((arg) => JSON.stringify(arg))
|
|
9
|
+
|
|
10
|
+
logger.log(`Swift: calling ${functionName} with args:`, jsonArgs)
|
|
11
|
+
|
|
12
|
+
const proc = Bun.spawn([binaryPath, functionName, ...jsonArgs], {
|
|
13
|
+
stdout: 'pipe',
|
|
14
|
+
stderr: 'inherit',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
const [stdout, exitCode] = await Promise.all([
|
|
18
|
+
new Response(proc.stdout).text(),
|
|
19
|
+
proc.exited,
|
|
20
|
+
])
|
|
21
|
+
|
|
22
|
+
if (exitCode !== 0) {
|
|
23
|
+
throw new Error(`Swift function "${functionName}" failed with exit code ${exitCode}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const trimmed = stdout.trim()
|
|
27
|
+
if (!trimmed) {
|
|
28
|
+
return undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return JSON.parse(trimmed)
|
|
33
|
+
} catch {
|
|
34
|
+
throw new Error(`Swift function "${functionName}" returned invalid JSON: ${trimmed}`)
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/utils/file-system.ts
CHANGED
|
@@ -8,6 +8,67 @@ export interface FileSystemItem {
|
|
|
8
8
|
isDirectory: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Recursively list all files in a directory (up to maxDepth levels)
|
|
13
|
+
*/
|
|
14
|
+
export async function listAllFiles({
|
|
15
|
+
basePath = '.',
|
|
16
|
+
maxDepth = 3,
|
|
17
|
+
maxFiles = 1000,
|
|
18
|
+
includeDirectories = true,
|
|
19
|
+
}: {
|
|
20
|
+
basePath?: string
|
|
21
|
+
maxDepth?: number
|
|
22
|
+
maxFiles?: number
|
|
23
|
+
includeDirectories?: boolean
|
|
24
|
+
} = {}): Promise<string[]> {
|
|
25
|
+
const results: string[] = []
|
|
26
|
+
|
|
27
|
+
// Resolve ~ to home directory
|
|
28
|
+
if (basePath.startsWith('~')) {
|
|
29
|
+
basePath = basePath.replace('~', os.homedir())
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const resolvedBase = path.isAbsolute(basePath)
|
|
33
|
+
? basePath
|
|
34
|
+
: path.resolve(process.cwd(), basePath)
|
|
35
|
+
|
|
36
|
+
async function walk(dir: string, depth: number) {
|
|
37
|
+
if (depth > maxDepth || results.length >= maxFiles) return
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
41
|
+
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (results.length >= maxFiles) break
|
|
44
|
+
|
|
45
|
+
// Skip hidden files and common ignored directories
|
|
46
|
+
if (entry.name.startsWith('.')) continue
|
|
47
|
+
if (entry.name === 'node_modules') continue
|
|
48
|
+
if (entry.name === 'dist') continue
|
|
49
|
+
if (entry.name === 'build') continue
|
|
50
|
+
|
|
51
|
+
const fullPath = path.join(dir, entry.name)
|
|
52
|
+
const relativePath = path.relative(resolvedBase, fullPath)
|
|
53
|
+
|
|
54
|
+
if (entry.isDirectory()) {
|
|
55
|
+
if (includeDirectories) {
|
|
56
|
+
results.push(relativePath + '/')
|
|
57
|
+
}
|
|
58
|
+
await walk(fullPath, depth + 1)
|
|
59
|
+
} else {
|
|
60
|
+
results.push(relativePath)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// Ignore permission errors etc
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await walk(resolvedBase, 0)
|
|
69
|
+
return results.sort()
|
|
70
|
+
}
|
|
71
|
+
|
|
11
72
|
export async function searchFiles(
|
|
12
73
|
searchPath: string,
|
|
13
74
|
prefix: string,
|