windows-development-sandbox 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/config.example.json +17 -1
- package/dist/cli/args.d.ts +13 -2
- package/dist/cli/args.js +95 -24
- package/dist/cli/commands/accounts.js +45 -17
- package/dist/cli/commands/context.d.ts +2 -0
- package/dist/cli/commands/context.js +9 -2
- package/dist/cli/commands/data/clean.js +32 -9
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/launch/add-root.d.ts +3 -2
- package/dist/cli/commands/launch/add-root.js +11 -10
- package/dist/cli/commands/launch/launch.js +57 -4
- package/dist/cli/commands/launch/prepare.js +3 -1
- package/dist/cli/commands/maintenance/audit.js +1 -1
- package/dist/cli/commands/maintenance/clean.d.ts +1 -1
- package/dist/cli/commands/maintenance/clean.js +17 -3
- package/dist/cli/commands/maintenance/config.d.ts +1 -1
- package/dist/cli/commands/maintenance/config.js +112 -10
- package/dist/cli/commands/maintenance/doctor.d.ts +1 -1
- package/dist/cli/commands/maintenance/doctor.js +2 -4
- package/dist/cli/commands/maintenance/migrate.d.ts +2 -1
- package/dist/cli/commands/maintenance/migrate.js +19 -4
- package/dist/cli/commands/maintenance/sync.js +2 -0
- package/dist/cli/commands/maintenance/update.d.ts +2 -0
- package/dist/cli/commands/maintenance/update.js +27 -4
- package/dist/cli/commands/projects/here.js +12 -7
- package/dist/cli/commands/projects/projects.js +26 -17
- package/dist/cli/commands/registry.js +7 -0
- package/dist/cli/commands/repair/checks/agents.d.ts +2 -0
- package/dist/cli/commands/repair/checks/agents.js +70 -0
- package/dist/cli/commands/repair/checks/common.d.ts +8 -0
- package/dist/cli/commands/repair/checks/common.js +22 -0
- package/dist/cli/commands/repair/checks/data.d.ts +2 -0
- package/dist/cli/commands/repair/checks/data.js +241 -0
- package/dist/cli/commands/repair/checks/index.d.ts +2 -0
- package/dist/cli/commands/repair/checks/index.js +20 -0
- package/dist/cli/commands/repair/checks/install.d.ts +5 -0
- package/dist/cli/commands/repair/checks/install.js +318 -0
- package/dist/cli/commands/repair/checks/machine.d.ts +2 -0
- package/dist/cli/commands/repair/checks/machine.js +232 -0
- package/dist/cli/commands/repair/checks/network.d.ts +6 -0
- package/dist/cli/commands/repair/checks/network.js +60 -0
- package/dist/cli/commands/repair/checks/pc.d.ts +4 -0
- package/dist/cli/commands/repair/checks/pc.js +55 -0
- package/dist/cli/commands/repair/checks/sandbox.d.ts +2 -0
- package/dist/cli/commands/repair/checks/sandbox.js +267 -0
- package/dist/cli/commands/repair/checks/settings.d.ts +2 -0
- package/dist/cli/commands/repair/checks/settings.js +169 -0
- package/dist/cli/commands/repair/checks/vpn.d.ts +2 -0
- package/dist/cli/commands/repair/checks/vpn.js +53 -0
- package/dist/cli/commands/repair/engine.d.ts +32 -0
- package/dist/cli/commands/repair/engine.js +127 -0
- package/dist/cli/commands/repair/env.d.ts +7 -0
- package/dist/cli/commands/repair/env.js +49 -0
- package/dist/cli/commands/repair/index.d.ts +1 -9
- package/dist/cli/commands/repair/index.js +2 -54
- package/dist/cli/commands/repair/log.d.ts +17 -0
- package/dist/cli/commands/repair/log.js +82 -0
- package/dist/cli/commands/repair/printer.d.ts +32 -0
- package/dist/cli/commands/repair/printer.js +132 -0
- package/dist/cli/commands/repair/run.d.ts +33 -0
- package/dist/cli/commands/repair/run.js +133 -0
- package/dist/cli/commands/repair/types.d.ts +97 -9
- package/dist/cli/commands/repair/types.js +11 -7
- package/dist/cli/commands/resume.js +2 -2
- package/dist/cli/commands/sessions/list.js +5 -2
- package/dist/cli/commands/sessions/status.d.ts +2 -0
- package/dist/cli/commands/sessions/status.js +27 -6
- package/dist/cli/commands/setup/index.d.ts +5 -0
- package/dist/cli/commands/setup/index.js +25 -1
- package/dist/cli/commands/storage/index.d.ts +2 -0
- package/dist/cli/commands/storage/index.js +2 -0
- package/dist/cli/commands/storage/offer.d.ts +3 -0
- package/dist/cli/commands/storage/offer.js +85 -0
- package/dist/cli/commands/storage/storage.d.ts +8 -0
- package/dist/cli/commands/storage/storage.js +171 -0
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.d.ts +4 -0
- package/dist/cli/commands/vpn.js +75 -0
- package/dist/cli/commands/web.d.ts +4 -0
- package/dist/cli/commands/web.js +95 -0
- package/dist/cli/history.js +1 -1
- package/dist/cli/index.js +46 -3
- package/dist/cli/manifest.d.ts +303 -29
- package/dist/cli/manifest.js +173 -43
- package/dist/cli/menu/add-agent.d.ts +47 -0
- package/dist/cli/menu/add-agent.js +168 -0
- package/dist/cli/menu/apps.d.ts +31 -2
- package/dist/cli/menu/apps.js +44 -5
- package/dist/cli/menu/command-fields.d.ts +18 -0
- package/dist/cli/menu/command-fields.js +61 -0
- package/dist/cli/menu/command-form.d.ts +22 -0
- package/dist/cli/menu/command-form.js +169 -46
- package/dist/cli/menu/data-folder.d.ts +18 -0
- package/dist/cli/menu/data-folder.js +99 -0
- package/dist/cli/menu/menu.d.ts +1 -1
- package/dist/cli/menu/menu.js +11 -8
- package/dist/cli/menu/pages/accounts-page.js +55 -24
- package/dist/cli/menu/pages/agents-page.d.ts +66 -0
- package/dist/cli/menu/pages/agents-page.js +338 -0
- package/dist/cli/menu/pages/apps-page.js +1 -2
- package/dist/cli/menu/pages/list-page.d.ts +16 -3
- package/dist/cli/menu/pages/list-page.js +40 -8
- package/dist/cli/menu/pages/maintenance-page.d.ts +9 -0
- package/dist/cli/menu/pages/maintenance-page.js +161 -72
- package/dist/cli/menu/pages/sandbox-page.d.ts +3 -0
- package/dist/cli/menu/pages/sandbox-page.js +132 -43
- package/dist/cli/menu/pages/sessions-page.d.ts +4 -0
- package/dist/cli/menu/pages/sessions-page.js +41 -25
- package/dist/cli/menu/pages/settings-page.d.ts +8 -4
- package/dist/cli/menu/pages/settings-page.js +36 -13
- package/dist/cli/menu/settings/categories.d.ts +2 -0
- package/dist/cli/menu/settings/categories.js +19 -44
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +7 -1
- package/dist/cli/menu/settings/collections/collection-editor.js +12 -8
- package/dist/cli/menu/settings/collections/map-editor.js +5 -1
- package/dist/cli/menu/settings/collections/object-list-editor.js +2 -2
- package/dist/cli/menu/settings/collections/object-map-editor.js +2 -2
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +11 -1
- package/dist/cli/menu/settings/collections/string-list-editor.js +23 -0
- package/dist/cli/menu/settings/index.d.ts +1 -1
- package/dist/cli/menu/settings/index.js +1 -1
- package/dist/cli/menu/settings/object-editor.d.ts +5 -1
- package/dist/cli/menu/settings/object-editor.js +15 -4
- package/dist/cli/menu/settings/path-kind.d.ts +20 -0
- package/dist/cli/menu/settings/path-kind.js +44 -0
- package/dist/cli/menu/settings/schema-node.d.ts +9 -0
- package/dist/cli/menu/settings/schema-node.js +26 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -0
- package/dist/cli/menu/settings/value-editor.js +51 -18
- package/dist/cli/menu/several.js +7 -5
- package/dist/cli/menu/shell.d.ts +37 -10
- package/dist/cli/menu/shell.js +90 -10
- package/dist/cli/menu/vpn-login.d.ts +16 -0
- package/dist/cli/menu/vpn-login.js +66 -0
- package/dist/cli/output/print.d.ts +2 -0
- package/dist/cli/output/print.js +8 -1
- package/dist/cli/services/accounts-service.d.ts +2 -0
- package/dist/cli/services/accounts-service.js +2 -2
- package/dist/cli/services/health-service.d.ts +0 -6
- package/dist/cli/services/health-service.js +5 -42
- package/dist/cli/services/index.d.ts +3 -1
- package/dist/cli/services/index.js +3 -1
- package/dist/cli/services/migrate-service.js +1 -1
- package/dist/cli/services/update-service.d.ts +2 -0
- package/dist/cli/services/update-service.js +13 -6
- package/dist/cli/services/vpn-service.d.ts +28 -0
- package/dist/cli/services/vpn-service.js +46 -0
- package/dist/cli/services/web-service.d.ts +22 -0
- package/dist/cli/services/web-service.js +111 -0
- package/dist/cli/tui/app.d.ts +6 -3
- package/dist/cli/tui/app.js +12 -5
- package/dist/cli/tui/dialogs/path-listing.d.ts +43 -0
- package/dist/cli/tui/dialogs/path-listing.js +127 -0
- package/dist/cli/tui/dialogs/path-picker.d.ts +67 -0
- package/dist/cli/tui/dialogs/path-picker.js +423 -0
- package/dist/cli/tui/dialogs/text-screen.d.ts +6 -0
- package/dist/cli/tui/dialogs/text-screen.js +10 -3
- package/dist/cli/tui/frame.d.ts +7 -10
- package/dist/cli/tui/frame.js +19 -10
- package/dist/cli/tui/index.d.ts +4 -3
- package/dist/cli/tui/index.js +2 -1
- package/dist/cli/tui/list/list-screen.d.ts +14 -2
- package/dist/cli/tui/list/list-screen.js +68 -32
- package/dist/cli/tui/list/list-types.d.ts +8 -4
- package/dist/cli/tui/nav.d.ts +29 -0
- package/dist/cli/tui/nav.js +67 -0
- package/dist/cli/validate.d.ts +1 -1
- package/dist/cli/validate.js +3 -3
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.js +11 -0
- package/dist/core/config/defaults.js +5 -1
- package/dist/core/config/loader.js +12 -2
- package/dist/core/local-config/overrides.js +1 -0
- package/dist/core/prompt.d.ts +19 -0
- package/dist/core/prompt.js +43 -3
- package/dist/core/schema/common.d.ts +6 -0
- package/dist/core/schema/common.js +8 -0
- package/dist/core/schema/config.d.ts +23 -0
- package/dist/core/schema/config.js +10 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +8 -2
- package/dist/core/schema/index.d.ts +3 -1
- package/dist/core/schema/index.js +3 -1
- package/dist/core/schema/profile.d.ts +1 -0
- package/dist/core/schema/profile.js +3 -0
- package/dist/core/schema/redact.d.ts +13 -0
- package/dist/core/schema/redact.js +75 -0
- package/dist/core/schema/sandbox.js +1 -0
- package/dist/core/schema/vpn.d.ts +17 -0
- package/dist/core/schema/vpn.js +22 -0
- package/dist/core/schema/web.d.ts +6 -0
- package/dist/core/schema/web.js +14 -0
- package/dist/core/types.d.ts +26 -0
- package/dist/guest/agents/agent-files.d.ts +49 -0
- package/dist/guest/agents/agent-files.js +63 -0
- package/dist/guest/agents/agent-installer.js +2 -9
- package/dist/guest/agents/local-state.d.ts +12 -0
- package/dist/guest/agents/local-state.js +198 -0
- package/dist/guest/agents/sbx-agent.d.ts +2 -0
- package/dist/guest/agents/sbx-agent.js +35 -0
- package/dist/guest/boot/boot.d.ts +4 -0
- package/dist/guest/boot/boot.js +50 -11
- package/dist/guest/daemon/daemon.js +4 -1
- package/dist/guest/daemon/exec.js +2 -1
- package/dist/guest/daemon/idle-shutdown.js +2 -0
- package/dist/guest/daemon/sessions.js +4 -1
- package/dist/guest/entries/agent.d.ts +1 -0
- package/dist/guest/entries/agent.js +10 -0
- package/dist/guest/entries/daemon.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/runtime/secrets.d.ts +12 -0
- package/dist/guest/runtime/secrets.js +63 -0
- package/dist/guest/setup/claude-profile.d.ts +2 -1
- package/dist/guest/setup/claude-profile.js +12 -1
- package/dist/guest/setup/environment.d.ts +6 -0
- package/dist/guest/setup/environment.js +56 -6
- package/dist/guest/setup/path-plan.d.ts +32 -0
- package/dist/guest/setup/path-plan.js +67 -0
- package/dist/guest/setup/shell-path.d.ts +1 -0
- package/dist/guest/setup/shell-path.js +22 -0
- package/dist/guest/toolchains/languages/python.js +1 -1
- package/dist/guest/toolchains/runtimes.js +2 -2
- package/dist/guest/vpn/index.d.ts +1 -0
- package/dist/guest/vpn/index.js +1 -0
- package/dist/guest/vpn/openvpn.d.ts +9 -0
- package/dist/guest/vpn/openvpn.js +47 -0
- package/dist/guest/vpn/tunnel.d.ts +17 -0
- package/dist/guest/vpn/tunnel.js +78 -0
- package/dist/guest/vpn/vpn.d.ts +6 -0
- package/dist/guest/vpn/vpn.js +126 -0
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +1 -1
- package/dist/host/channel/channel.d.ts +7 -0
- package/dist/host/channel/channel.js +26 -2
- package/dist/host/health/acl-read.d.ts +17 -0
- package/dist/host/health/acl-read.js +45 -0
- package/dist/host/health/config-backup.d.ts +11 -0
- package/dist/host/health/config-backup.js +73 -0
- package/dist/host/health/index.d.ts +6 -0
- package/dist/host/health/index.js +7 -0
- package/dist/host/health/install-state.d.ts +33 -0
- package/dist/host/health/install-state.js +110 -0
- package/dist/host/health/path-copies.d.ts +32 -0
- package/dist/host/health/path-copies.js +82 -0
- package/dist/host/health/stale.d.ts +45 -0
- package/dist/host/health/stale.js +120 -0
- package/dist/host/health/system.d.ts +37 -0
- package/dist/host/health/system.js +87 -0
- package/dist/host/install/copies.d.ts +13 -0
- package/dist/host/install/copies.js +63 -0
- package/dist/host/install/index.d.ts +1 -0
- package/dist/host/install/index.js +1 -0
- package/dist/host/launch/generate.js +25 -1
- package/dist/host/launch/session-file.js +4 -1
- package/dist/host/launch/stage.js +1 -1
- package/dist/host/migrate/backup.js +3 -0
- package/dist/host/migrate/migrations/config-file.d.ts +2 -0
- package/dist/host/migrate/migrations/config-file.js +7 -0
- package/dist/host/migrate/migrations/follow-machine-storage.js +9 -1
- package/dist/host/migrate/migrations/pin-storage-root.d.ts +4 -0
- package/dist/host/migrate/migrations/pin-storage-root.js +46 -0
- package/dist/host/migrate/migrations/unpin-config-defaults.js +4 -1
- package/dist/host/migrate/registry.js +2 -1
- package/dist/host/profile/accounts.d.ts +25 -1
- package/dist/host/profile/accounts.js +96 -13
- package/dist/host/profile/agent-accounts.d.ts +5 -1
- package/dist/host/profile/agent-accounts.js +35 -5
- package/dist/host/profile/index.d.ts +2 -1
- package/dist/host/profile/index.js +1 -0
- package/dist/host/profile/plugin-share.d.ts +26 -0
- package/dist/host/profile/plugin-share.js +146 -0
- package/dist/host/projects/join.d.ts +7 -1
- package/dist/host/projects/join.js +21 -4
- package/dist/host/projects/registry.d.ts +16 -10
- package/dist/host/projects/registry.js +74 -6
- package/dist/host/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +21 -2
- package/dist/host/secrets/index.d.ts +2 -0
- package/dist/host/secrets/index.js +2 -0
- package/dist/host/secrets/launch-secrets.d.ts +17 -0
- package/dist/host/secrets/launch-secrets.js +93 -0
- package/dist/host/secrets/secret-store.d.ts +25 -0
- package/dist/host/secrets/secret-store.js +89 -0
- package/dist/host/sharing/acl.d.ts +16 -2
- package/dist/host/sharing/acl.js +35 -10
- package/dist/host/sharing/current.js +11 -2
- package/dist/host/sharing/doctor.d.ts +2 -0
- package/dist/host/sharing/doctor.js +44 -0
- package/dist/host/sharing/group-access.d.ts +6 -0
- package/dist/host/sharing/group-access.js +24 -0
- package/dist/host/sharing/index.d.ts +2 -1
- package/dist/host/sharing/index.js +2 -1
- package/dist/host/sharing/lifecycle.d.ts +8 -2
- package/dist/host/sharing/lifecycle.js +16 -7
- package/dist/host/storage/cleanup.d.ts +2 -0
- package/dist/host/storage/cleanup.js +2 -1
- package/dist/host/storage/history.d.ts +21 -0
- package/dist/host/storage/history.js +49 -0
- package/dist/host/storage/index.d.ts +4 -0
- package/dist/host/storage/index.js +4 -0
- package/dist/host/storage/move.d.ts +95 -0
- package/dist/host/storage/move.js +384 -0
- package/dist/host/storage/offer.d.ts +11 -0
- package/dist/host/storage/offer.js +36 -0
- package/dist/host/storage/root.d.ts +18 -0
- package/dist/host/storage/root.js +77 -0
- package/dist/host/tools/windows-terminal.d.ts +2 -0
- package/dist/host/tools/windows-terminal.js +23 -5
- package/dist/host/update/index.d.ts +1 -1
- package/dist/host/update/index.js +1 -1
- package/dist/host/update/notice.d.ts +2 -0
- package/dist/host/update/notice.js +13 -1
- package/dist/host/update/update-state.d.ts +6 -0
- package/dist/host/update/update-state.js +2 -0
- package/dist/host/update/updater.d.ts +19 -0
- package/dist/host/update/updater.js +88 -12
- package/dist/host/vpn/index.d.ts +4 -0
- package/dist/host/vpn/index.js +4 -0
- package/dist/host/vpn/login.d.ts +24 -0
- package/dist/host/vpn/login.js +79 -0
- package/dist/host/vpn/nordvpn.d.ts +21 -0
- package/dist/host/vpn/nordvpn.js +77 -0
- package/dist/host/vpn/prepare.d.ts +17 -0
- package/dist/host/vpn/prepare.js +77 -0
- package/dist/host/vpn/status.d.ts +12 -0
- package/dist/host/vpn/status.js +53 -0
- package/dist/host/web/auth.d.ts +38 -0
- package/dist/host/web/auth.js +96 -0
- package/dist/host/web/backend.d.ts +14 -0
- package/dist/host/web/backend.js +1 -0
- package/dist/host/web/bridge.d.ts +27 -0
- package/dist/host/web/bridge.js +144 -0
- package/dist/host/web/browser.d.ts +1 -0
- package/dist/host/web/browser.js +10 -0
- package/dist/host/web/control.d.ts +17 -0
- package/dist/host/web/control.js +67 -0
- package/dist/host/web/index.d.ts +9 -0
- package/dist/host/web/index.js +8 -0
- package/dist/host/web/runner.d.ts +14 -0
- package/dist/host/web/runner.js +45 -0
- package/dist/host/web/server.d.ts +40 -0
- package/dist/host/web/server.js +218 -0
- package/dist/host/web/state.d.ts +20 -0
- package/dist/host/web/state.js +41 -0
- package/dist/host/web/static-files.d.ts +13 -0
- package/dist/host/web/static-files.js +57 -0
- package/dist/runtime/agent.js +27614 -0
- package/dist/runtime/daemon.js +1012 -618
- package/dist/runtime/db.js +236 -106
- package/dist/runtime/main.js +1093 -250
- package/dist/runtime/netproxy.js +102 -3
- package/dist/runtime/view.js +102 -3
- package/dist/selftest/accounts.test.js +40 -0
- package/dist/selftest/agent-paths.test.d.ts +2 -0
- package/dist/selftest/agent-paths.test.js +115 -0
- package/dist/selftest/channel.test.js +5 -0
- package/dist/selftest/cli.test.js +34 -4
- package/dist/selftest/credentials.test.d.ts +2 -0
- package/dist/selftest/credentials.test.js +198 -0
- package/dist/selftest/generate.test.js +1 -1
- package/dist/selftest/health.test.d.ts +2 -0
- package/dist/selftest/health.test.js +470 -0
- package/dist/selftest/host-sim.test.js +2 -0
- package/dist/selftest/index.js +19 -0
- package/dist/selftest/layout.test.js +2 -0
- package/dist/selftest/menu-screens.d.ts +7 -0
- package/dist/selftest/menu-screens.js +65 -16
- package/dist/selftest/menu.test.js +574 -21
- package/dist/selftest/migrate.test.js +1 -1
- package/dist/selftest/personalization.test.js +3 -1
- package/dist/selftest/projects.test.js +39 -1
- package/dist/selftest/shared-use.test.js +64 -2
- package/dist/selftest/sharing.test.js +50 -1
- package/dist/selftest/storage.test.d.ts +2 -0
- package/dist/selftest/storage.test.js +363 -0
- package/dist/selftest/support/fixtures.d.ts +14 -0
- package/dist/selftest/support/releases.js +1 -1
- package/dist/selftest/toolchains.test.js +21 -0
- package/dist/selftest/update-service.test.js +87 -3
- package/dist/selftest/vpn.test.d.ts +2 -0
- package/dist/selftest/vpn.test.js +279 -0
- package/dist/selftest/web.test.d.ts +2 -0
- package/dist/selftest/web.test.js +304 -0
- package/dist/shared/apps/catalogue.js +31 -0
- package/dist/shared/channel/index.d.ts +1 -0
- package/dist/shared/channel/index.js +1 -0
- package/dist/shared/channel/run-dir.d.ts +6 -1
- package/dist/shared/channel/run-dir.js +12 -1
- package/dist/shared/channel/schema.d.ts +19 -0
- package/dist/shared/channel/schema.js +11 -0
- package/dist/shared/channel/secrets.d.ts +28 -0
- package/dist/shared/channel/secrets.js +44 -0
- package/dist/shared/vpn/download.d.ts +13 -0
- package/dist/shared/vpn/download.js +14 -0
- package/dist/shared/vpn/kill-switch.d.ts +18 -0
- package/dist/shared/vpn/kill-switch.js +81 -0
- package/dist/shared/vpn/ovpn.d.ts +23 -0
- package/dist/shared/vpn/ovpn.js +244 -0
- package/dist/shared/web/index.d.ts +1 -0
- package/dist/shared/web/index.js +1 -0
- package/dist/shared/web/protocol.d.ts +61 -0
- package/dist/shared/web/protocol.js +35 -0
- package/dist/sync/sync-cycle.js +5 -1
- package/dist/sync/sync-state.d.ts +1 -0
- package/dist/sync/sync-state.js +1 -0
- package/dist/sync/watcher.js +10 -0
- package/dist/web/index.html +13 -0
- package/dist/web/main-CXESL347.js +45 -0
- package/dist/web/main-VLR7DVQ4.css +1 -0
- package/native/csharp/FolderSecurity.cs +29 -0
- package/native/scripts/protect-data.ps1 +29 -0
- package/native/scripts/read-folder-sddl.ps1 +10 -0
- package/native/scripts/set-folder-access.ps1 +49 -7
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1421 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +15 -5
- package/sandbox/csharp/EnvBroadcast.cs +33 -0
- package/sandbox/csharp/PathSync.cs +90 -0
- package/sandbox/scripts/install-shell-path.ps1 +73 -0
- package/sandbox/scripts/set-machine-environment.ps1 +37 -4
- package/schemas/config.schema.json +79 -4
- package/schemas/sandbox-config.schema.json +14 -1
- package/dist/cli/commands/repair/checks.d.ts +0 -2
- package/dist/cli/commands/repair/checks.js +0 -246
- package/dist/cli/menu/pages/start-page.d.ts +0 -43
- package/dist/cli/menu/pages/start-page.js +0 -192
- package/dist/cli/tui/tab-bar.d.ts +0 -9
- package/dist/cli/tui/tab-bar.js +0 -33
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
// A full screen for choosing a folder (or a file): the path on top (type it, Tab completes), the folders below to
|
|
2
|
+
// browse with the arrow keys, drives with their free space, new folders, a filter. Links are shown but never opened.
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { freeBytesAt, humanBytes } from '../../../core/disk.js';
|
|
6
|
+
import { Text } from '../text.js';
|
|
7
|
+
import { Theme } from '../theme.js';
|
|
8
|
+
import { paragraphs } from './paragraphs.js';
|
|
9
|
+
import { completeInput, folderNameProblem, listDrives, listFolder, narrow, parentOf, splitInput, withSeparator, } from './path-listing.js';
|
|
10
|
+
const win = path.win32;
|
|
11
|
+
/** Resolves to the chosen path, or undefined when you press Esc. */
|
|
12
|
+
export class PathPickerScreen {
|
|
13
|
+
o;
|
|
14
|
+
mode = 'input';
|
|
15
|
+
input;
|
|
16
|
+
cursor;
|
|
17
|
+
dir = '';
|
|
18
|
+
listing = { entries: [], more: 0 };
|
|
19
|
+
loadedDir = null;
|
|
20
|
+
selected = 0;
|
|
21
|
+
scroll = 0;
|
|
22
|
+
filter = '';
|
|
23
|
+
newName = '';
|
|
24
|
+
problem;
|
|
25
|
+
notice;
|
|
26
|
+
rows = 10;
|
|
27
|
+
constructor(o) {
|
|
28
|
+
this.o = o;
|
|
29
|
+
this.input = o.initial ?? '';
|
|
30
|
+
this.cursor = this.input.length;
|
|
31
|
+
this.syncListing();
|
|
32
|
+
}
|
|
33
|
+
render(size) {
|
|
34
|
+
const width = Math.min(size.width - 4, 100);
|
|
35
|
+
const inner = width - 4;
|
|
36
|
+
const typing = this.mode === 'input';
|
|
37
|
+
const frame = (t) => (typing ? Theme.accent(t) : Theme.muted(t));
|
|
38
|
+
const start = Math.max(0, this.cursor - inner + 1);
|
|
39
|
+
const shown = this.input.slice(start, start + inner);
|
|
40
|
+
const before = shown.slice(0, this.cursor - start);
|
|
41
|
+
const at = shown[this.cursor - start] ?? ' ';
|
|
42
|
+
const after = shown.slice(this.cursor - start + 1);
|
|
43
|
+
const cursor = typing ? (Theme.colour ? '\x1b[7m' + at + '\x1b[27m' : '_') : at;
|
|
44
|
+
const field = this.input === '' && !typing ? Theme.muted('type a path or pick below') : before + cursor + after;
|
|
45
|
+
const verdict = this.verdict();
|
|
46
|
+
const head = [
|
|
47
|
+
'',
|
|
48
|
+
' ' + Theme.bold(this.o.title),
|
|
49
|
+
...(this.o.about?.length ? paragraphs(this.o.about, size.width).map((l) => Theme.muted(l)) : []),
|
|
50
|
+
' ' + frame('╭' + '─'.repeat(width - 2) + '╮'),
|
|
51
|
+
' ' + frame('│') + ' ' + Text.fit(field, inner) + ' ' + frame('│'),
|
|
52
|
+
' ' + frame('╰' + '─'.repeat(width - 2) + '╯'),
|
|
53
|
+
' ' + this.verdictLine(verdict),
|
|
54
|
+
' ' + Theme.muted(this.where()),
|
|
55
|
+
];
|
|
56
|
+
const tail = this.mode === 'name' ? 3 : 0;
|
|
57
|
+
this.rows = Math.max(3, size.height - head.length - tail - 1);
|
|
58
|
+
const visible = this.visible();
|
|
59
|
+
this.keepSelectedInView(visible.length);
|
|
60
|
+
const lines = visible
|
|
61
|
+
.slice(this.scroll, this.scroll + this.rows)
|
|
62
|
+
.map((e, i) => this.row(e, this.scroll + i, size.width));
|
|
63
|
+
if (!visible.length)
|
|
64
|
+
lines.push(' ' + Theme.muted(this.listing.error ?? (this.filter ? 'Nothing matches the filter.' : 'Nothing here.')));
|
|
65
|
+
if (this.listing.more && this.scroll + this.rows >= visible.length)
|
|
66
|
+
lines.push(' ' +
|
|
67
|
+
Theme.muted(Theme.glyph.more + ' ' + this.listing.more + ' more (type more of the name to narrow the list)'));
|
|
68
|
+
const body = [...head, ...lines];
|
|
69
|
+
if (this.mode === 'name')
|
|
70
|
+
body.push('', ' New folder name: ' + this.newName + (Theme.colour ? '\x1b[7m \x1b[27m' : '_'));
|
|
71
|
+
return { path: this.o.path, body, keys: this.keys(), notice: this.notice };
|
|
72
|
+
}
|
|
73
|
+
async handle(key) {
|
|
74
|
+
this.notice = undefined;
|
|
75
|
+
if (key.ctrl && key.name === 's')
|
|
76
|
+
return this.choose(this.candidate());
|
|
77
|
+
if (key.ctrl && key.name === 'return')
|
|
78
|
+
return this.choose(this.candidate());
|
|
79
|
+
if (key.ctrl && key.name === 'n')
|
|
80
|
+
return this.startName();
|
|
81
|
+
switch (this.mode) {
|
|
82
|
+
case 'input':
|
|
83
|
+
return this.typing(key);
|
|
84
|
+
case 'browse':
|
|
85
|
+
return this.browsing(key);
|
|
86
|
+
case 'filter':
|
|
87
|
+
return this.filtering(key);
|
|
88
|
+
case 'name':
|
|
89
|
+
return this.naming(key);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ---- what is shown ----
|
|
93
|
+
/** The entries on screen: the folder's, narrowed by what is typed after the last backslash and by the filter. */
|
|
94
|
+
visible() {
|
|
95
|
+
const prefix = this.mode === 'input' || this.dirIsTyped() ? splitInput(this.input).prefix : '';
|
|
96
|
+
const base = narrow(this.listing.entries, prefix, this.filter);
|
|
97
|
+
const up = this.dir === '' ? [] : [{ kind: 'up', name: '..', path: parentOf(this.dir) }];
|
|
98
|
+
return this.filter || prefix ? base : [...up, ...base];
|
|
99
|
+
}
|
|
100
|
+
dirIsTyped() {
|
|
101
|
+
return win.resolve(splitInput(this.input).dir || '.').toLowerCase() === win.resolve(this.dir || '.').toLowerCase();
|
|
102
|
+
}
|
|
103
|
+
row(e, index, width) {
|
|
104
|
+
const on = this.mode !== 'input' && index === this.selected;
|
|
105
|
+
const mark = on ? Theme.glyph.pointer : ' ';
|
|
106
|
+
let text;
|
|
107
|
+
switch (e.kind) {
|
|
108
|
+
case 'drive':
|
|
109
|
+
text = e.name + ' ' + Theme.muted(e.detail ?? '');
|
|
110
|
+
break;
|
|
111
|
+
case 'up':
|
|
112
|
+
text = Theme.glyph.back + ' ' + Theme.muted('up one folder');
|
|
113
|
+
break;
|
|
114
|
+
case 'dir':
|
|
115
|
+
text = Theme.glyph.folder + ' ' + e.name;
|
|
116
|
+
break;
|
|
117
|
+
case 'link':
|
|
118
|
+
text = Theme.muted(Theme.glyph.folder + ' ' + e.name + ' (a link: not opened)');
|
|
119
|
+
break;
|
|
120
|
+
case 'file':
|
|
121
|
+
text = Theme.muted(' ' + e.name);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
const line = ' ' + mark + ' ' + text;
|
|
125
|
+
return on ? Theme.selected(Text.fit(line, Math.min(width - 2, 100))) : line;
|
|
126
|
+
}
|
|
127
|
+
where() {
|
|
128
|
+
if (this.dir === '')
|
|
129
|
+
return 'This PC: pick a drive';
|
|
130
|
+
const free = freeBytesAt(this.dir);
|
|
131
|
+
const count = this.listing.entries.length + (this.listing.more || 0);
|
|
132
|
+
const drive = win.parse(win.resolve(this.dir)).root;
|
|
133
|
+
return (this.dir +
|
|
134
|
+
' ' +
|
|
135
|
+
Theme.glyph.dot +
|
|
136
|
+
' ' +
|
|
137
|
+
count +
|
|
138
|
+
(this.o.files ? ' items' : ' folders') +
|
|
139
|
+
(free === null ? '' : ' ' + Theme.glyph.dot + ' ' + humanBytes(free) + ' free on ' + drive));
|
|
140
|
+
}
|
|
141
|
+
verdict() {
|
|
142
|
+
const target = this.candidate();
|
|
143
|
+
if (!target)
|
|
144
|
+
return 'Type a ' + (this.o.files ? 'file' : 'folder') + ', or pick one below.';
|
|
145
|
+
if (this.kept(target))
|
|
146
|
+
return this.o.validate?.(target) ?? true;
|
|
147
|
+
if (!win.isAbsolute(target))
|
|
148
|
+
return 'Type a full path, like D:\\Sandbox.';
|
|
149
|
+
return this.o.validate?.(target) ?? true;
|
|
150
|
+
}
|
|
151
|
+
verdictLine(verdict) {
|
|
152
|
+
if (this.problem)
|
|
153
|
+
return Theme.error(Theme.glyph.cross + ' ' + this.problem);
|
|
154
|
+
const target = this.candidate();
|
|
155
|
+
if (verdict === true)
|
|
156
|
+
return (Theme.ok(Theme.glyph.ok) + ' ' + Theme.muted((this.o.use ? 'Will ' + this.o.use + ': ' : 'Will use: ') + target));
|
|
157
|
+
return target ? Theme.warn(Theme.glyph.cross + ' ' + verdict) : Theme.muted(verdict);
|
|
158
|
+
}
|
|
159
|
+
keys() {
|
|
160
|
+
const use = this.o.use ?? 'use this folder';
|
|
161
|
+
switch (this.mode) {
|
|
162
|
+
case 'input':
|
|
163
|
+
return [
|
|
164
|
+
['↵', 'choose', 0],
|
|
165
|
+
['tab', 'complete'],
|
|
166
|
+
['↓', 'browse'],
|
|
167
|
+
['esc', 'cancel', 0],
|
|
168
|
+
['ctrl+u', 'clear', 9],
|
|
169
|
+
];
|
|
170
|
+
case 'browse':
|
|
171
|
+
return [
|
|
172
|
+
['↵ →', 'open', 1],
|
|
173
|
+
['←', 'up', 2],
|
|
174
|
+
['s', use, 0],
|
|
175
|
+
['n', 'new folder', 3],
|
|
176
|
+
['/', 'filter', 4],
|
|
177
|
+
['tab', 'type a path', 5],
|
|
178
|
+
['esc', 'cancel', 0],
|
|
179
|
+
];
|
|
180
|
+
case 'filter':
|
|
181
|
+
return [
|
|
182
|
+
['↵', 'keep filter'],
|
|
183
|
+
['esc', 'clear', 0],
|
|
184
|
+
];
|
|
185
|
+
case 'name':
|
|
186
|
+
return [
|
|
187
|
+
['↵', 'create', 0],
|
|
188
|
+
['esc', 'cancel', 0],
|
|
189
|
+
];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ---- what a path means ----
|
|
193
|
+
/** What choosing now would pick. In the browser a highlighted folder (or file) wins over the folder being shown. */
|
|
194
|
+
candidate() {
|
|
195
|
+
if (this.mode === 'browse' || this.mode === 'filter') {
|
|
196
|
+
const entry = this.visible()[this.selected];
|
|
197
|
+
if (entry && (entry.kind === 'dir' || (entry.kind === 'file' && this.o.files)))
|
|
198
|
+
return entry.path;
|
|
199
|
+
return this.dir === '' ? '' : this.dir.replace(/[\\/]+$/, '') || this.dir;
|
|
200
|
+
}
|
|
201
|
+
const text = this.input.trim();
|
|
202
|
+
return text.length > 3 ? text.replace(/[\\/]+$/, '') : text;
|
|
203
|
+
}
|
|
204
|
+
choose(target) {
|
|
205
|
+
const verdict = this.verdict();
|
|
206
|
+
if (verdict !== true) {
|
|
207
|
+
this.problem = verdict;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
return { done: this.kept(target) ? target : win.resolve(target) };
|
|
211
|
+
}
|
|
212
|
+
/** Is this typed text kept as typed: one of the allowed words, or a relative path where those are allowed? */
|
|
213
|
+
kept(target) {
|
|
214
|
+
return (this.o.literals ?? []).includes(target) || Boolean(this.o.relative && !win.isAbsolute(target));
|
|
215
|
+
}
|
|
216
|
+
// ---- the listing ----
|
|
217
|
+
/** Reads the folder the input names (only when it changed), keeping what was typed as a filter. */
|
|
218
|
+
syncListing() {
|
|
219
|
+
const { dir } = splitInput(this.input);
|
|
220
|
+
this.openDir(dir === '' || fs.existsSync(dir) ? dir : this.dir === '' ? '' : this.dir);
|
|
221
|
+
}
|
|
222
|
+
openDir(dir) {
|
|
223
|
+
if (dir === this.loadedDir)
|
|
224
|
+
return;
|
|
225
|
+
this.loadedDir = dir;
|
|
226
|
+
this.dir = dir;
|
|
227
|
+
this.listing = dir === '' ? { entries: listDrives(), more: 0 } : listFolder(dir, this.o.files ?? false);
|
|
228
|
+
this.selected = 0;
|
|
229
|
+
this.scroll = 0;
|
|
230
|
+
}
|
|
231
|
+
go(dir) {
|
|
232
|
+
this.filter = '';
|
|
233
|
+
this.openDir(dir);
|
|
234
|
+
this.input = withSeparator(dir);
|
|
235
|
+
this.cursor = this.input.length;
|
|
236
|
+
this.problem = undefined;
|
|
237
|
+
}
|
|
238
|
+
keepSelectedInView(count) {
|
|
239
|
+
this.selected = Math.max(0, Math.min(count - 1, this.selected));
|
|
240
|
+
if (this.selected < this.scroll)
|
|
241
|
+
this.scroll = this.selected;
|
|
242
|
+
if (this.selected >= this.scroll + this.rows)
|
|
243
|
+
this.scroll = this.selected - this.rows + 1;
|
|
244
|
+
this.scroll = Math.max(0, Math.min(this.scroll, Math.max(0, count - this.rows)));
|
|
245
|
+
}
|
|
246
|
+
// ---- keys ----
|
|
247
|
+
typing(key) {
|
|
248
|
+
if (key.name === 'escape')
|
|
249
|
+
return { done: undefined };
|
|
250
|
+
if (key.name === 'return')
|
|
251
|
+
return this.enterInput();
|
|
252
|
+
if (key.name === 'down' || key.name === 'pagedown') {
|
|
253
|
+
this.mode = 'browse';
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (key.name === 'tab') {
|
|
257
|
+
const next = completeInput(this.input, this.listing.entries);
|
|
258
|
+
this.edit(next, next.length);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (key.name === 'left')
|
|
262
|
+
this.cursor = Math.max(0, this.cursor - 1);
|
|
263
|
+
else if (key.name === 'right')
|
|
264
|
+
this.cursor = Math.min(this.input.length, this.cursor + 1);
|
|
265
|
+
else if (key.name === 'home')
|
|
266
|
+
this.cursor = 0;
|
|
267
|
+
else if (key.name === 'end')
|
|
268
|
+
this.cursor = this.input.length;
|
|
269
|
+
else if (key.name === 'backspace') {
|
|
270
|
+
if (this.cursor > 0)
|
|
271
|
+
this.edit(this.input.slice(0, this.cursor - 1) + this.input.slice(this.cursor), this.cursor - 1);
|
|
272
|
+
}
|
|
273
|
+
else if (key.name === 'delete')
|
|
274
|
+
this.edit(this.input.slice(0, this.cursor) + this.input.slice(this.cursor + 1), this.cursor);
|
|
275
|
+
else if (key.ctrl && key.name === 'u')
|
|
276
|
+
this.edit('', 0);
|
|
277
|
+
else if (key.text) {
|
|
278
|
+
const add = key.text.replace(/[\r\n]/g, '').replace(/\//g, '\\');
|
|
279
|
+
this.edit(this.input.slice(0, this.cursor) + add + this.input.slice(this.cursor), this.cursor + add.length);
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
edit(next, cursor) {
|
|
284
|
+
this.input = next;
|
|
285
|
+
this.cursor = Math.max(0, Math.min(next.length, cursor));
|
|
286
|
+
this.problem = undefined;
|
|
287
|
+
this.syncListing();
|
|
288
|
+
this.selected = 0;
|
|
289
|
+
this.scroll = 0;
|
|
290
|
+
}
|
|
291
|
+
/** A path ending in a backslash is opened to look inside; any other typed path is chosen. */
|
|
292
|
+
enterInput() {
|
|
293
|
+
const text = this.input.trim();
|
|
294
|
+
if (text === '') {
|
|
295
|
+
this.problem = 'Type a ' + (this.o.files ? 'file' : 'folder') + ', or press ↓ to browse.';
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (/[\\/]$/.test(text) || /^[A-Za-z]:$/.test(text)) {
|
|
299
|
+
const { dir } = splitInput(text);
|
|
300
|
+
if (fs.existsSync(dir))
|
|
301
|
+
this.go(dir);
|
|
302
|
+
else
|
|
303
|
+
this.problem = 'No such folder: ' + dir;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
return this.choose(this.candidate());
|
|
307
|
+
}
|
|
308
|
+
browsing(key) {
|
|
309
|
+
const list = this.visible();
|
|
310
|
+
const move = (by) => {
|
|
311
|
+
this.selected = Math.max(0, Math.min(list.length - 1, this.selected + by));
|
|
312
|
+
};
|
|
313
|
+
if (key.name === 'escape')
|
|
314
|
+
return { done: undefined };
|
|
315
|
+
if (key.name === 'up') {
|
|
316
|
+
if (this.selected === 0)
|
|
317
|
+
this.mode = 'input';
|
|
318
|
+
else
|
|
319
|
+
move(-1);
|
|
320
|
+
}
|
|
321
|
+
else if (key.name === 'down')
|
|
322
|
+
move(1);
|
|
323
|
+
else if (key.name === 'pageup')
|
|
324
|
+
move(-this.rows);
|
|
325
|
+
else if (key.name === 'pagedown')
|
|
326
|
+
move(this.rows);
|
|
327
|
+
else if (key.name === 'home')
|
|
328
|
+
this.selected = 0;
|
|
329
|
+
else if (key.name === 'end')
|
|
330
|
+
this.selected = Math.max(0, list.length - 1);
|
|
331
|
+
else if (key.name === 'return' || key.name === 'right')
|
|
332
|
+
return this.open(list[this.selected]);
|
|
333
|
+
else if (key.name === 'left' || key.name === 'backspace')
|
|
334
|
+
this.go(parentOf(this.dir));
|
|
335
|
+
else if (key.name === 'tab')
|
|
336
|
+
this.mode = 'input';
|
|
337
|
+
else if (key.text === 's')
|
|
338
|
+
return this.choose(this.candidate());
|
|
339
|
+
else if (key.text === 'n')
|
|
340
|
+
return this.startName();
|
|
341
|
+
else if (key.text === '/') {
|
|
342
|
+
this.mode = 'filter';
|
|
343
|
+
this.filter = '';
|
|
344
|
+
this.selected = 0;
|
|
345
|
+
}
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
open(entry) {
|
|
349
|
+
if (!entry)
|
|
350
|
+
return;
|
|
351
|
+
if (entry.kind === 'link') {
|
|
352
|
+
this.notice = 'That is a link; it is not opened. Pick a normal folder.';
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
if (entry.kind === 'file')
|
|
356
|
+
return this.choose(entry.path);
|
|
357
|
+
this.go(entry.path);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
filtering(key) {
|
|
361
|
+
if (key.name === 'escape') {
|
|
362
|
+
this.filter = '';
|
|
363
|
+
this.mode = 'browse';
|
|
364
|
+
this.selected = 0;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (key.name === 'return') {
|
|
368
|
+
this.mode = 'browse';
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (key.name === 'up')
|
|
372
|
+
this.selected = Math.max(0, this.selected - 1);
|
|
373
|
+
else if (key.name === 'down')
|
|
374
|
+
this.selected++;
|
|
375
|
+
else if (key.name === 'backspace') {
|
|
376
|
+
this.filter = this.filter.slice(0, -1);
|
|
377
|
+
this.selected = 0;
|
|
378
|
+
}
|
|
379
|
+
else if (key.text) {
|
|
380
|
+
this.filter += key.text;
|
|
381
|
+
this.selected = 0;
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
startName() {
|
|
386
|
+
if (this.dir === '') {
|
|
387
|
+
this.notice = 'Open a drive first.';
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
this.mode = 'name';
|
|
391
|
+
this.newName = '';
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
naming(key) {
|
|
395
|
+
if (key.name === 'escape') {
|
|
396
|
+
this.mode = 'browse';
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (key.name === 'backspace')
|
|
400
|
+
this.newName = this.newName.slice(0, -1);
|
|
401
|
+
else if (key.name === 'return') {
|
|
402
|
+
const problem = folderNameProblem(this.newName);
|
|
403
|
+
if (problem) {
|
|
404
|
+
this.notice = problem;
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
const made = win.join(this.dir, this.newName.trim());
|
|
408
|
+
try {
|
|
409
|
+
fs.mkdirSync(made);
|
|
410
|
+
}
|
|
411
|
+
catch (e) {
|
|
412
|
+
this.notice = 'Could not create it: ' + (e instanceof Error ? e.message : String(e));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
this.mode = 'browse';
|
|
416
|
+
this.loadedDir = null;
|
|
417
|
+
this.go(made);
|
|
418
|
+
}
|
|
419
|
+
else if (key.text)
|
|
420
|
+
this.newName += key.text;
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
@@ -12,6 +12,12 @@ export interface TextOptions {
|
|
|
12
12
|
validate?: (value: string) => true | string;
|
|
13
13
|
/** Allow an empty answer (otherwise Enter on an empty field cancels). */
|
|
14
14
|
allowEmpty?: boolean;
|
|
15
|
+
/** Only characters this matches can be typed (a number field takes digits only). */
|
|
16
|
+
accept?: RegExp;
|
|
17
|
+
/** A muted line under the field while nothing is wrong (what is allowed: "1 to 65536"). */
|
|
18
|
+
hint?: string;
|
|
19
|
+
/** A secret: the field shows * for each character (a password, an API key). */
|
|
20
|
+
masked?: boolean;
|
|
15
21
|
}
|
|
16
22
|
/** One line of text. Resolves to the text, or undefined when you press Esc. */
|
|
17
23
|
export declare class TextScreen implements Screen<string | undefined> {
|
|
@@ -14,7 +14,7 @@ export class TextScreen {
|
|
|
14
14
|
}
|
|
15
15
|
render(size) {
|
|
16
16
|
const width = Math.min(size.width - 4, 100);
|
|
17
|
-
const chars = [...this.value];
|
|
17
|
+
const chars = [...this.value].map((ch) => (this.o.masked ? '*' : ch));
|
|
18
18
|
// Scroll the field so the cursor stays visible.
|
|
19
19
|
const inner = width - 4;
|
|
20
20
|
const start = Math.max(0, this.cursor - inner + 1);
|
|
@@ -33,7 +33,11 @@ export class TextScreen {
|
|
|
33
33
|
' ' + Theme.accent('╭' + '─'.repeat(width - 2) + '╮'),
|
|
34
34
|
' ' + Theme.accent('│') + ' ' + Text.fit(text, inner) + ' ' + Theme.accent('│'),
|
|
35
35
|
' ' + Theme.accent('╰' + '─'.repeat(width - 2) + '╯'),
|
|
36
|
-
this.problem
|
|
36
|
+
this.problem
|
|
37
|
+
? ' ' + Theme.error(Theme.glyph.cross + ' ' + this.problem)
|
|
38
|
+
: this.o.hint
|
|
39
|
+
? ' ' + Theme.muted(this.o.hint)
|
|
40
|
+
: '',
|
|
37
41
|
];
|
|
38
42
|
return {
|
|
39
43
|
path: this.o.path,
|
|
@@ -95,7 +99,10 @@ export class TextScreen {
|
|
|
95
99
|
return;
|
|
96
100
|
}
|
|
97
101
|
if (key.text) {
|
|
98
|
-
const
|
|
102
|
+
const accept = this.o.accept;
|
|
103
|
+
const add = [...key.text.replace(/[\r\n]/g, '')].filter((c) => !accept || accept.test(c));
|
|
104
|
+
if (!add.length)
|
|
105
|
+
return;
|
|
99
106
|
set([...chars.slice(0, this.cursor), ...add, ...chars.slice(this.cursor)], this.cursor + add.length);
|
|
100
107
|
}
|
|
101
108
|
return;
|
package/dist/cli/tui/frame.d.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { type KeyHint } from './key-bar.js';
|
|
2
|
-
import { type
|
|
3
|
-
/** The tabs across the top and which one is open. */
|
|
4
|
-
export interface TabsState {
|
|
5
|
-
readonly tabs: readonly TabInfo[];
|
|
6
|
-
readonly active: number;
|
|
7
|
-
}
|
|
2
|
+
import { type NavState } from './nav.js';
|
|
8
3
|
export interface FrameParts {
|
|
9
4
|
/** Where you are, e.g. ['Settings', 'Languages and tools']. */
|
|
10
5
|
path: string[];
|
|
@@ -15,13 +10,15 @@ export interface FrameParts {
|
|
|
15
10
|
keys: KeyHint[];
|
|
16
11
|
/** A message for the last action (saved, invalid ...), shown above the footer. */
|
|
17
12
|
notice?: string;
|
|
18
|
-
/**
|
|
19
|
-
|
|
13
|
+
/** The navigation on the left (added by the app for every screen of the menu). */
|
|
14
|
+
nav?: NavState;
|
|
20
15
|
}
|
|
21
16
|
export declare class Frame {
|
|
22
17
|
static title: string;
|
|
23
18
|
static version: string;
|
|
24
|
-
/** Rows the body may use on a terminal `height` rows high (title, status, rules, notice, keys: 6
|
|
25
|
-
static bodyHeight(height: number
|
|
19
|
+
/** Rows the body may use on a terminal `height` rows high (title, status, rules, notice, keys: 6). */
|
|
20
|
+
static bodyHeight(height: number): number;
|
|
21
|
+
/** Columns the body may use on a terminal `width` columns wide: what the navigation and its divider leave. */
|
|
22
|
+
static bodyWidth(width: number, nav?: NavState): number;
|
|
26
23
|
static render(parts: FrameParts, width: number, height: number): string[];
|
|
27
24
|
}
|
package/dist/cli/tui/frame.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
// The frame every screen is drawn in: a title bar with where you are,
|
|
2
|
-
// and a footer with the keys that work here.
|
|
1
|
+
// The frame every screen is drawn in: a title bar with where you are, a status line, the navigation on the left, the
|
|
2
|
+
// screen's own body and a footer with the keys that work here.
|
|
3
3
|
import { KeyBar } from './key-bar.js';
|
|
4
|
-
import {
|
|
4
|
+
import { SideNav } from './nav.js';
|
|
5
5
|
import { Text } from './text.js';
|
|
6
6
|
import { Theme } from './theme.js';
|
|
7
7
|
export class Frame {
|
|
8
8
|
static title = 'Windows Development Sandbox';
|
|
9
9
|
static version = '';
|
|
10
|
-
/** Rows the body may use on a terminal `height` rows high (title, status, rules, notice, keys: 6
|
|
11
|
-
static bodyHeight(height
|
|
12
|
-
return Math.max(3, height - 6
|
|
10
|
+
/** Rows the body may use on a terminal `height` rows high (title, status, rules, notice, keys: 6). */
|
|
11
|
+
static bodyHeight(height) {
|
|
12
|
+
return Math.max(3, height - 6);
|
|
13
|
+
}
|
|
14
|
+
/** Columns the body may use on a terminal `width` columns wide: what the navigation and its divider leave. */
|
|
15
|
+
static bodyWidth(width, nav) {
|
|
16
|
+
return nav ? Math.max(20, width - SideNav.width(width) - 1) : width;
|
|
13
17
|
}
|
|
14
18
|
static render(parts, width, height) {
|
|
15
19
|
const w = width - 1;
|
|
@@ -18,14 +22,19 @@ export class Frame {
|
|
|
18
22
|
.join(Theme.dim(' › '));
|
|
19
23
|
const right = Frame.version ? 'v' + Frame.version + ' ' : '';
|
|
20
24
|
const title = Theme.bar(Text.fit(' ' + Theme.accent('◆') + ' ' + crumbs, w - Text.width(right)) + right);
|
|
21
|
-
const tabs = parts.tabs ? [TabBar.render(parts.tabs.tabs, parts.tabs.active, width)] : [];
|
|
22
25
|
const status = ' ' + Text.truncate(parts.status ?? '', w - 2);
|
|
23
|
-
const
|
|
24
|
-
const
|
|
26
|
+
const bodyRows = Frame.bodyHeight(height);
|
|
27
|
+
const navWidth = parts.nav ? SideNav.width(width) : 0;
|
|
28
|
+
const line = (joint) => Theme.dim(navWidth && navWidth + 1 < w
|
|
29
|
+
? Theme.glyph.line.repeat(navWidth) + joint + Theme.glyph.line.repeat(w - navWidth - 1)
|
|
30
|
+
: Theme.glyph.line.repeat(w));
|
|
31
|
+
const side = parts.nav ? SideNav.render(parts.nav, width, bodyRows) : [];
|
|
32
|
+
const divider = Theme.dim(Theme.glyph.vline);
|
|
25
33
|
const body = parts.body.slice(0, bodyRows);
|
|
26
34
|
while (body.length < bodyRows)
|
|
27
35
|
body.push('');
|
|
36
|
+
const rows = parts.nav ? body.map((row, i) => (side[i] ?? ' '.repeat(navWidth)) + divider + row) : body;
|
|
28
37
|
const notice = parts.notice ? ' ' + Text.truncate(parts.notice, w - 2) : '';
|
|
29
|
-
return [title,
|
|
38
|
+
return [title, status, line('┬'), ...rows, notice, line('┴'), KeyBar.render(parts.keys, width)];
|
|
30
39
|
}
|
|
31
40
|
}
|
package/dist/cli/tui/index.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ export { type Screen, type Size, type Step } from './screen.js';
|
|
|
3
3
|
export { ConfirmScreen } from './dialogs/confirm-screen.js';
|
|
4
4
|
export { MessageScreen } from './dialogs/message-screen.js';
|
|
5
5
|
export { dialogBox, OverlayScreen, overlayBox, type OverlayChoice, type OverlayOptions } from './dialogs/overlay.js';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
6
|
+
export { PathPickerScreen, type PathPickerOptions } from './dialogs/path-picker.js';
|
|
7
|
+
export { TextScreen, type TextOptions } from './dialogs/text-screen.js';
|
|
8
|
+
export { Frame, type FrameParts } from './frame.js';
|
|
8
9
|
export { KeyBar, type KeyHint } from './key-bar.js';
|
|
9
|
-
export {
|
|
10
|
+
export { SideNav, type NavCategory, type NavSection, type NavState } from './nav.js';
|
|
10
11
|
export { ListScreen } from './list/list-screen.js';
|
|
11
12
|
export { type Category, type ListAction, type ListItem, type ListOptions, type ListResult, type Tab, } from './list/list-types.js';
|
|
12
13
|
export { ExitRequested, Terminal, type Key } from './terminal.js';
|
package/dist/cli/tui/index.js
CHANGED
|
@@ -3,10 +3,11 @@ export { App } from './app.js';
|
|
|
3
3
|
export { ConfirmScreen } from './dialogs/confirm-screen.js';
|
|
4
4
|
export { MessageScreen } from './dialogs/message-screen.js';
|
|
5
5
|
export { dialogBox, OverlayScreen, overlayBox } from './dialogs/overlay.js';
|
|
6
|
+
export { PathPickerScreen } from './dialogs/path-picker.js';
|
|
6
7
|
export { TextScreen } from './dialogs/text-screen.js';
|
|
7
8
|
export { Frame } from './frame.js';
|
|
8
9
|
export { KeyBar } from './key-bar.js';
|
|
9
|
-
export {
|
|
10
|
+
export { SideNav } from './nav.js';
|
|
10
11
|
export { ListScreen } from './list/list-screen.js';
|
|
11
12
|
export { ExitRequested, Terminal } from './terminal.js';
|
|
12
13
|
export { Text } from './text.js';
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FrameParts } from '../frame.js';
|
|
2
2
|
import type { Screen, Size, Step } from '../screen.js';
|
|
3
3
|
import type { Key } from '../terminal.js';
|
|
4
|
-
import type { ListResult, ListOptions } from './list-types.js';
|
|
4
|
+
import type { Category, ListResult, ListOptions } from './list-types.js';
|
|
5
5
|
export declare class ListScreen<T> implements Screen<ListResult<T>> {
|
|
6
6
|
private readonly o;
|
|
7
7
|
private category;
|
|
8
8
|
private focus;
|
|
9
|
+
/** The list has the keyboard (false while the menu's navigation has it): the highlight is drawn softer. */
|
|
10
|
+
private active;
|
|
9
11
|
private cursor;
|
|
10
12
|
private offset;
|
|
11
13
|
private query;
|
|
@@ -24,9 +26,19 @@ export declare class ListScreen<T> implements Screen<ListResult<T>> {
|
|
|
24
26
|
private keys;
|
|
25
27
|
/** Programmatic notice (e.g. "Saved") shown until the next key. */
|
|
26
28
|
setNotice(text: string | undefined): void;
|
|
29
|
+
/** The categories are shown by the menu's navigation, which drives them (no sidebar of its own). */
|
|
30
|
+
private get external();
|
|
31
|
+
private enterLabel;
|
|
32
|
+
private valueAt;
|
|
33
|
+
/** The categories of the list (empty without any). */
|
|
34
|
+
get categories(): readonly Category[];
|
|
35
|
+
/** Opens a category (the menu's navigation does this); a search in progress ends. */
|
|
36
|
+
setCategory(id: string): void;
|
|
37
|
+
/** Whether the list has the keyboard (the highlight is drawn softer when it has not). */
|
|
38
|
+
setActive(on: boolean): void;
|
|
27
39
|
/** The id of the category open in the sidebar (undefined without a sidebar). */
|
|
28
40
|
get currentCategory(): string | undefined;
|
|
29
|
-
/** The extra keys that work now (they may depend on the category). */
|
|
41
|
+
/** The extra keys that work now (they may depend on the category and on the highlighted entry). */
|
|
30
42
|
private actionsNow;
|
|
31
43
|
/** Keys are going into the search box (digits and letters are text, not shortcuts). */
|
|
32
44
|
get isSearching(): boolean;
|