@webship.co/workspace 1.0.0-beta1 → 1.0.0-rc1
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/LICENSE +339 -0
- package/README.md +28 -364
- package/core/bin/workspace.js +135 -0
- package/core/config/settings.labels.yml +514 -0
- package/core/config/settings.template.yml +292 -0
- package/core/config/style.yml +53 -0
- package/core/config/theme.default.yml +161 -0
- package/core/config/theme.dispatch.yml +166 -0
- package/core/config/theme.drupal.yml +175 -0
- package/core/config/theme.ember.yml +160 -0
- package/core/config/theme.github.yml +172 -0
- package/core/config/theme.gitlab.yml +168 -0
- package/core/config/theme.press.yml +161 -0
- package/core/config/theme.slate.yml +160 -0
- package/core/config/theme.upsun.yml +174 -0
- package/core/docs/architecture.svg +65 -0
- package/core/docs/getting-started.md +51 -0
- package/core/docs/plugins.md +27 -0
- package/core/docs/releases.md +23 -0
- package/core/docs/testing.md +15 -0
- package/core/docs/workspaces.md +25 -0
- package/core/scripts/args/arg-ai-items.sh +65 -0
- package/core/scripts/args/arg-autostart.sh +31 -0
- package/core/scripts/args/arg-avatar-agent.sh +28 -0
- package/core/scripts/args/arg-avatar-catalog.sh +12 -0
- package/core/scripts/args/arg-avatar-character.sh +68 -0
- package/core/scripts/args/arg-avatar-voice.sh +24 -0
- package/core/scripts/args/arg-avatar.sh +22 -0
- package/core/scripts/args/arg-checks.sh +36 -0
- package/core/scripts/args/arg-clone.sh +42 -0
- package/core/scripts/args/arg-commands.sh +51 -0
- package/core/scripts/args/arg-cucumber.sh +34 -0
- package/core/scripts/args/arg-distribution.sh +30 -0
- package/core/scripts/args/arg-drupal.sh +39 -0
- package/core/scripts/args/arg-drupal_cms.sh +18 -0
- package/core/scripts/args/arg-gitlab-ci-local.sh +25 -0
- package/core/scripts/args/arg-graphify.sh +36 -0
- package/core/scripts/args/arg-graphs.sh +12 -0
- package/core/scripts/args/arg-hostnames.sh +23 -0
- package/core/scripts/args/arg-milvus-backup.sh +8 -0
- package/core/scripts/args/arg-milvus-mcp.sh +12 -0
- package/core/scripts/args/arg-milvus.sh +32 -0
- package/core/scripts/args/arg-obsidian.sh +17 -0
- package/core/scripts/args/arg-odoo.sh +18 -0
- package/core/scripts/args/arg-penpot-backup.sh +8 -0
- package/core/scripts/args/arg-penpot-mcp.sh +15 -0
- package/core/scripts/args/arg-penpot-token.sh +17 -0
- package/core/scripts/args/arg-penpot.sh +44 -0
- package/core/scripts/args/arg-rag.sh +21 -0
- package/core/scripts/args/arg-ragify.sh +35 -0
- package/core/scripts/args/arg-reclaim.sh +18 -0
- package/core/scripts/args/arg-release.sh +21 -0
- package/core/scripts/args/arg-screen.sh +28 -0
- package/core/scripts/args/arg-site-template.sh +13 -0
- package/core/scripts/args/arg-spdd.sh +16 -0
- package/core/scripts/args/arg-specs.sh +22 -0
- package/core/scripts/args/arg-storage.sh +36 -0
- package/core/scripts/args/arg-testing.sh +36 -0
- package/core/scripts/args/arg-webship.sh +34 -0
- package/core/scripts/args/arg-webships-project.sh +22 -0
- package/core/scripts/args/arg-webships-site-template.sh +17 -0
- package/core/scripts/args/arg-webships.sh +34 -0
- package/core/scripts/args/arg-website-site-template.sh +17 -0
- package/core/scripts/args/arg-website.sh +25 -0
- package/core/scripts/args/arg-wordpress.sh +18 -0
- package/core/scripts/autostart-run.sh +162 -0
- package/core/scripts/autostart-stop.sh +30 -0
- package/core/scripts/autostart-watch.sh +43 -0
- package/core/scripts/autostart.sh +26 -0
- package/core/scripts/bootstrap-functions.sh +117 -0
- package/core/scripts/bootstrap-libraries.sh +10 -0
- package/core/scripts/bootstrap.sh +71 -0
- package/core/scripts/ddev-shim/ddev +122 -0
- package/core/scripts/docker-gid.sh +119 -0
- package/core/scripts/functions/arg-site-template.sh +28 -0
- package/core/scripts/functions/fun-ai-items-lock.sh +175 -0
- package/core/scripts/functions/fun-ai-items-sources.sh +298 -0
- package/core/scripts/functions/fun-ai-items.sh +621 -0
- package/core/scripts/functions/fun-ai-providers.sh +72 -0
- package/core/scripts/functions/fun-autostart.sh +429 -0
- package/core/scripts/functions/fun-avatar.sh +441 -0
- package/core/scripts/functions/fun-backup-item.sh +50 -0
- package/core/scripts/functions/fun-build-distribution.sh +226 -0
- package/core/scripts/functions/fun-build-site-template.sh +104 -0
- package/core/scripts/functions/fun-build-webships.sh +161 -0
- package/core/scripts/functions/fun-build-website.sh +199 -0
- package/core/scripts/functions/fun-checks.sh +243 -0
- package/core/scripts/functions/fun-clone-project.sh +289 -0
- package/core/scripts/functions/fun-commands.sh +521 -0
- package/core/scripts/functions/fun-database.sh +63 -0
- package/core/scripts/functions/fun-ddev-hostnames.sh +213 -0
- package/core/scripts/functions/fun-ddev-nodejs.sh +15 -0
- package/core/scripts/functions/fun-ddev-telemetry.sh +15 -0
- package/core/scripts/functions/fun-default-settings.sh +28 -0
- package/core/scripts/functions/fun-distribution-cucumber.sh +45 -0
- package/core/scripts/functions/fun-distribution-drupal.sh +25 -0
- package/core/scripts/functions/fun-distribution-drupal_cms.sh +35 -0
- package/core/scripts/functions/fun-distribution-webship.sh +28 -0
- package/core/scripts/functions/fun-distribution-webships.sh +33 -0
- package/core/scripts/functions/fun-distribution-website.sh +19 -0
- package/core/scripts/functions/fun-drush.sh +48 -0
- package/core/scripts/functions/fun-elevenlabs.sh +179 -0
- package/core/scripts/functions/fun-files.sh +19 -0
- package/core/scripts/functions/fun-git-worktrees.sh +142 -0
- package/core/scripts/functions/fun-gitlab-ci.sh +156 -0
- package/core/scripts/functions/fun-hostnames-cli.sh +88 -0
- package/core/scripts/functions/fun-milvus.sh +584 -0
- package/core/scripts/functions/fun-odoo.sh +121 -0
- package/core/scripts/functions/fun-penpot.sh +724 -0
- package/core/scripts/functions/fun-project-name.sh +20 -0
- package/core/scripts/functions/fun-rag.sh +253 -0
- package/core/scripts/functions/fun-ragify.sh +165 -0
- package/core/scripts/functions/fun-reclaim.sh +297 -0
- package/core/scripts/functions/fun-release.sh +159 -0
- package/core/scripts/functions/fun-remove-project.sh +319 -0
- package/core/scripts/functions/fun-screen.sh +96 -0
- package/core/scripts/functions/fun-spdd.sh +676 -0
- package/core/scripts/functions/fun-specs-share.sh +237 -0
- package/core/scripts/functions/fun-storage.sh +347 -0
- package/core/scripts/functions/fun-testing.sh +329 -0
- package/core/scripts/functions/fun-users.sh +87 -0
- package/core/scripts/functions/fun-version.sh +72 -0
- package/core/scripts/functions/fun-video-edit.sh +48 -0
- package/core/scripts/functions/fun-voice.sh +45 -0
- package/core/scripts/functions/fun-wordpress.sh +59 -0
- package/core/scripts/host-mounts.sh +161 -0
- package/core/scripts/install.sh +602 -0
- package/core/scripts/libs/__pycache__/video_edit.cpython-312.pyc +0 -0
- package/core/scripts/libs/argparse.sh +93 -0
- package/core/scripts/libs/bash-yaml.sh +52 -0
- package/core/scripts/libs/checks_plan.js +312 -0
- package/core/scripts/libs/elevenlabs.py +627 -0
- package/core/scripts/libs/progress-bar.sh +19 -0
- package/core/scripts/libs/rag_index.py +446 -0
- package/core/scripts/libs/video_edit.py +636 -0
- package/core/scripts/libs/voice.py +430 -0
- package/core/scripts/plugins/jira/host-install.sh +119 -0
- package/core/scripts/plugins/jira/host-mounts.txt +2 -0
- package/core/scripts/plugins/upsun/args/arg-upsun.sh +51 -0
- package/core/scripts/plugins/upsun/functions/fun-upsun.sh +304 -0
- package/core/scripts/plugins/upsun/host-install.sh +99 -0
- package/core/scripts/plugins/upsun/host-mounts.txt +1 -0
- package/core/scripts/plugins/upsun/upsun-ai.sh +133 -0
- package/core/scripts/plugins.sh +152 -0
- package/core/scripts/release.sh +69 -0
- package/core/scripts/storage.sh +39 -0
- package/core/scripts/sync-tooling.sh +246 -0
- package/core/scripts/tests/cmd-smoke-test.sh +109 -0
- package/core/scripts/trust-https.sh +257 -0
- package/core/workspace-app/.ddev/commands/web/test-dashboard +49 -0
- package/core/workspace-app/.ddev/config.yaml +400 -0
- package/core/workspace-app/.ddev/docker-compose.dood.yaml +45 -0
- package/core/workspace-app/.ddev/docker-compose.plugin-jira-twg.yaml +26 -0
- package/core/workspace-app/.ddev/docker-compose.plugin-upsun-upsun-cli.yaml +8 -0
- package/core/workspace-app/.ddev/docker-compose.screens.yaml +15 -0
- package/core/workspace-app/.ddev/nginx_full/nginx-site.conf +114 -0
- package/core/workspace-app/.ddev/web-build/Dockerfile.workspace-app +203 -0
- package/core/workspace-app/.ddev/web-build/ddev-shim-ddev +122 -0
- package/{PLUGINS.md → core/workspace-app/PLUGINS.md} +9 -1
- package/core/workspace-app/README.md +380 -0
- package/{actions → core/workspace-app/actions}/ddev.js +0 -1
- package/{actions → core/workspace-app/actions}/rag.js +0 -1
- package/{actions → core/workspace-app/actions}/testing.js +0 -1
- package/{assistant.js → core/workspace-app/assistant.js} +1 -10
- package/{git-info.js → core/workspace-app/git-info.js} +17 -1
- package/core/workspace-app/package.json +29 -0
- package/core/workspace-app/plugin-catalog.js +50 -0
- package/{plugins-page.js → core/workspace-app/plugins-page.js} +28 -2
- package/{server.js → core/workspace-app/server.js} +18 -0
- package/core/workspace-app/tests/features/actions-rail.feature +44 -0
- package/core/workspace-app/tests/features/ai-items.feature +23 -0
- package/core/workspace-app/tests/features/assistant-speech.feature +63 -0
- package/core/workspace-app/tests/features/checks.feature +40 -0
- package/core/workspace-app/tests/features/clone-project.feature +30 -0
- package/core/workspace-app/tests/features/command-authoring.feature +58 -0
- package/core/workspace-app/tests/features/dashboard.feature +57 -0
- package/core/workspace-app/tests/features/menus.feature +29 -0
- package/core/workspace-app/tests/features/plugins/jira/jira.feature +47 -0
- package/core/workspace-app/tests/features/plugins.feature +20 -0
- package/core/workspace-app/tests/features/project-actions.feature +41 -0
- package/core/workspace-app/tests/features/screens.feature +29 -0
- package/core/workspace-app/tests/features/settings.feature +46 -0
- package/core/workspace-app/tests/features/test-reports.feature +32 -0
- package/core/workspace-app/tests/features/theme.feature +40 -0
- package/core/workspace-app/tests/step-definitions/actions-post.steps.js +25 -0
- package/core/workspace-app/tests/step-definitions/assistant-speech.steps.js +157 -0
- package/core/workspace-app/tests/step-definitions/clone-project.steps.js +47 -0
- package/core/workspace-app/tests/step-definitions/preconditions.steps.js +168 -0
- package/core/workspace-app/tests/unit/_isolated-config.js +31 -0
- package/core/workspace-app/tests/unit/accessibility.test.js +124 -0
- package/core/workspace-app/tests/unit/actions-rail-ux.test.js +56 -0
- package/core/workspace-app/tests/unit/actions-rail.test.js +51 -0
- package/core/workspace-app/tests/unit/ai-inventory.test.js +64 -0
- package/core/workspace-app/tests/unit/app-docs.test.js +64 -0
- package/core/workspace-app/tests/unit/assistant-avatar.test.js +110 -0
- package/core/workspace-app/tests/unit/assistant-cast.test.js +105 -0
- package/core/workspace-app/tests/unit/assistant-send-animation.test.js +15 -0
- package/core/workspace-app/tests/unit/assistant-suggestions.test.js +112 -0
- package/core/workspace-app/tests/unit/assistant-switches.test.js +53 -0
- package/core/workspace-app/tests/unit/assistant-views.test.js +146 -0
- package/core/workspace-app/tests/unit/assistant-working-indicator.test.js +49 -0
- package/core/workspace-app/tests/unit/assistant.test.js +61 -0
- package/core/workspace-app/tests/unit/audio-player.test.js +147 -0
- package/core/workspace-app/tests/unit/audio-voice.test.js +634 -0
- package/core/workspace-app/tests/unit/auth-google.test.js +180 -0
- package/core/workspace-app/tests/unit/autostart-watch.test.js +76 -0
- package/core/workspace-app/tests/unit/browsers.test.js +76 -0
- package/core/workspace-app/tests/unit/build-distribution.test.js +125 -0
- package/core/workspace-app/tests/unit/build-order.test.js +153 -0
- package/core/workspace-app/tests/unit/builder-args.test.js +113 -0
- package/core/workspace-app/tests/unit/builders.test.js +158 -0
- package/core/workspace-app/tests/unit/card-state-toggle.test.js +29 -0
- package/core/workspace-app/tests/unit/changelog.test.js +223 -0
- package/core/workspace-app/tests/unit/chat-thread.test.js +92 -0
- package/core/workspace-app/tests/unit/checks-plan.test.js +205 -0
- package/core/workspace-app/tests/unit/claude-args.test.js +182 -0
- package/core/workspace-app/tests/unit/claude-code-here.test.js +15 -0
- package/core/workspace-app/tests/unit/claude-resume.test.js +147 -0
- package/core/workspace-app/tests/unit/claude-screen-options.test.js +84 -0
- package/core/workspace-app/tests/unit/claude-session.test.js +41 -0
- package/core/workspace-app/tests/unit/command-flags.test.js +45 -0
- package/core/workspace-app/tests/unit/command-palette.test.js +105 -0
- package/core/workspace-app/tests/unit/commands-center.test.js +338 -0
- package/core/workspace-app/tests/unit/composer-menu.test.js +187 -0
- package/core/workspace-app/tests/unit/config-yaml.test.js +116 -0
- package/core/workspace-app/tests/unit/confirm-dialog.test.js +69 -0
- package/core/workspace-app/tests/unit/create-card.test.js +85 -0
- package/core/workspace-app/tests/unit/ddev-addons.test.js +144 -0
- package/core/workspace-app/tests/unit/ddev-auth-ssh.test.js +40 -0
- package/core/workspace-app/tests/unit/ddev-custom-commands.test.js +56 -0
- package/core/workspace-app/tests/unit/ddev-diagnose.test.js +57 -0
- package/core/workspace-app/tests/unit/ddev-domains.test.js +179 -0
- package/core/workspace-app/tests/unit/ddev-hostnames.test.js +143 -0
- package/core/workspace-app/tests/unit/ddev-import.test.js +82 -0
- package/core/workspace-app/tests/unit/ddev-mail.test.js +135 -0
- package/core/workspace-app/tests/unit/ddev-menu-and-create.test.js +28 -0
- package/core/workspace-app/tests/unit/ddev-mutagen.test.js +37 -0
- package/core/workspace-app/tests/unit/ddev-options.test.js +51 -0
- package/core/workspace-app/tests/unit/ddev-share.test.js +40 -0
- package/core/workspace-app/tests/unit/ddev-shim.test.js +57 -0
- package/core/workspace-app/tests/unit/ddev-snapshots.test.js +142 -0
- package/core/workspace-app/tests/unit/ddev-start-daemons.test.js +62 -0
- package/core/workspace-app/tests/unit/ddev-xdebug.test.js +41 -0
- package/core/workspace-app/tests/unit/deep-chat-origin.test.js +38 -0
- package/core/workspace-app/tests/unit/density.test.js +52 -0
- package/core/workspace-app/tests/unit/elevenlabs.test.js +453 -0
- package/core/workspace-app/tests/unit/event-log.test.js +102 -0
- package/core/workspace-app/tests/unit/favicon.test.js +68 -0
- package/core/workspace-app/tests/unit/files-browser.test.js +224 -0
- package/core/workspace-app/tests/unit/git-info.test.js +26 -0
- package/core/workspace-app/tests/unit/grid-and-list.test.js +103 -0
- package/core/workspace-app/tests/unit/host-tool-wrapper.test.js +18 -0
- package/core/workspace-app/tests/unit/html-patterns.test.js +51 -0
- package/core/workspace-app/tests/unit/ide-infra.test.js +28 -0
- package/core/workspace-app/tests/unit/images-ai.test.js +201 -0
- package/core/workspace-app/tests/unit/images-create.test.js +59 -0
- package/core/workspace-app/tests/unit/images-providers.test.js +248 -0
- package/core/workspace-app/tests/unit/images-svg.test.js +213 -0
- package/core/workspace-app/tests/unit/images-workspace.test.js +144 -0
- package/core/workspace-app/tests/unit/jobs-fragment.test.js +66 -0
- package/core/workspace-app/tests/unit/media-ux.test.js +164 -0
- package/core/workspace-app/tests/unit/modal-fit.test.js +87 -0
- package/core/workspace-app/tests/unit/modules-load.test.js +23 -0
- package/core/workspace-app/tests/unit/narration.test.js +186 -0
- package/core/workspace-app/tests/unit/navigation-editor.test.js +260 -0
- package/core/workspace-app/tests/unit/navigation.test.js +254 -0
- package/core/workspace-app/tests/unit/new-folder.test.js +121 -0
- package/core/workspace-app/tests/unit/new-screen-form.test.js +149 -0
- package/core/workspace-app/tests/unit/node-menu.test.js +161 -0
- package/core/workspace-app/tests/unit/notes.test.js +128 -0
- package/core/workspace-app/tests/unit/page-structure.test.js +40 -0
- package/core/workspace-app/tests/unit/page-width.test.js +59 -0
- package/core/workspace-app/tests/unit/php-version.test.js +90 -0
- package/core/workspace-app/tests/unit/plan-card.test.js +44 -0
- package/core/workspace-app/tests/unit/plugin-catalog.test.js +31 -0
- package/core/workspace-app/tests/unit/plugin-system.test.js +251 -0
- package/core/workspace-app/tests/unit/plugins-page.test.js +28 -0
- package/core/workspace-app/tests/unit/plugins.test.js +58 -0
- package/core/workspace-app/tests/unit/preconditions.test.js +21 -0
- package/core/workspace-app/tests/unit/project-activity-filter.test.js +180 -0
- package/core/workspace-app/tests/unit/project-make.test.js +143 -0
- package/core/workspace-app/tests/unit/project-name.test.js +202 -0
- package/core/workspace-app/tests/unit/reader.test.js +62 -0
- package/core/workspace-app/tests/unit/release.test.js +92 -0
- package/core/workspace-app/tests/unit/remove-confirm.test.js +37 -0
- package/core/workspace-app/tests/unit/report-pdf.test.js +171 -0
- package/core/workspace-app/tests/unit/round-11.test.js +31 -0
- package/core/workspace-app/tests/unit/round-12.test.js +37 -0
- package/core/workspace-app/tests/unit/row-layout.test.js +585 -0
- package/core/workspace-app/tests/unit/row-menus-fold.test.js +29 -0
- package/core/workspace-app/tests/unit/rows-add-modal.test.js +68 -0
- package/core/workspace-app/tests/unit/screen-runner-queue.test.js +41 -0
- package/core/workspace-app/tests/unit/screen-window.test.js +116 -0
- package/core/workspace-app/tests/unit/screens-beside-running.test.js +19 -0
- package/core/workspace-app/tests/unit/screens-columns.test.js +79 -0
- package/core/workspace-app/tests/unit/screens-menu.test.js +84 -0
- package/core/workspace-app/tests/unit/screens-modal-overlay.test.js +14 -0
- package/core/workspace-app/tests/unit/screens-waiting.test.js +129 -0
- package/core/workspace-app/tests/unit/screens.test.js +93 -0
- package/core/workspace-app/tests/unit/second-storage.test.js +218 -0
- package/core/workspace-app/tests/unit/session-name.test.js +64 -0
- package/core/workspace-app/tests/unit/settings-file.test.js +173 -0
- package/core/workspace-app/tests/unit/settings-nested-list.test.js +54 -0
- package/core/workspace-app/tests/unit/settings-raw.test.js +177 -0
- package/core/workspace-app/tests/unit/shared-prefs.test.js +49 -0
- package/core/workspace-app/tests/unit/speech.test.js +290 -0
- package/core/workspace-app/tests/unit/static-caching.test.js +39 -0
- package/core/workspace-app/tests/unit/status-language.test.js +47 -0
- package/core/workspace-app/tests/unit/tab-modal.test.js +69 -0
- package/core/workspace-app/tests/unit/tab-title.test.js +37 -0
- package/core/workspace-app/tests/unit/template-literals.test.js +52 -0
- package/core/workspace-app/tests/unit/terminal-scrollback.test.js +61 -0
- package/core/workspace-app/tests/unit/terminal-size.test.js +103 -0
- package/core/workspace-app/tests/unit/terminal-style.test.js +84 -0
- package/core/workspace-app/tests/unit/terminal-windows.test.js +154 -0
- package/core/workspace-app/tests/unit/themes.test.js +453 -0
- package/core/workspace-app/tests/unit/tool-labels.test.js +34 -0
- package/core/workspace-app/tests/unit/topbar-icons.test.js +47 -0
- package/core/workspace-app/tests/unit/update-docker-gid.test.js +51 -0
- package/core/workspace-app/tests/unit/user-guide.test.js +60 -0
- package/core/workspace-app/tests/unit/version-check.test.js +98 -0
- package/core/workspace-app/tests/unit/version-link.test.js +50 -0
- package/core/workspace-app/tests/unit/video-borrow.test.js +220 -0
- package/core/workspace-app/tests/unit/video-edit-ask.test.js +93 -0
- package/core/workspace-app/tests/unit/video-edit-short.test.js +42 -0
- package/core/workspace-app/tests/unit/video-edit.test.js +436 -0
- package/core/workspace-app/tests/unit/video-transcript.test.js +237 -0
- package/core/workspace-app/tests/unit/waiting.test.js +48 -0
- package/core/workspace-app/tests/unit/wordpress-odoo-builders.test.js +38 -0
- package/core/workspace-app/tests/unit/workspace-bin.test.js +48 -0
- package/core/workspace-app/tests/unit/workspace-card-tools.test.js +30 -0
- package/core/workspace-app/tests/unit/workspace-delete.test.js +77 -0
- package/core/workspace-app/tests/unit/workspace-types.test.js +68 -0
- package/core/workspace-app/tests/unit/worktree-clone.test.js +250 -0
- package/package.json +38 -36
- /package/{WORKSPACES.md → core/workspace-app/WORKSPACES.md} +0 -0
- /package/{actions → core/workspace-app/actions}/ai-items.js +0 -0
- /package/{actions → core/workspace-app/actions}/audio.js +0 -0
- /package/{actions → core/workspace-app/actions}/build-order.js +0 -0
- /package/{actions → core/workspace-app/actions}/checks.js +0 -0
- /package/{actions → core/workspace-app/actions}/claude-session.js +0 -0
- /package/{actions → core/workspace-app/actions}/commands-center.js +0 -0
- /package/{actions → core/workspace-app/actions}/composer.js +0 -0
- /package/{actions → core/workspace-app/actions}/ddev-addons.js +0 -0
- /package/{actions → core/workspace-app/actions}/ddev-mail.js +0 -0
- /package/{actions → core/workspace-app/actions}/folders.js +0 -0
- /package/{actions → core/workspace-app/actions}/images-svg.js +0 -0
- /package/{actions → core/workspace-app/actions}/images.js +0 -0
- /package/{actions → core/workspace-app/actions}/navigation.js +0 -0
- /package/{actions → core/workspace-app/actions}/node.js +0 -0
- /package/{actions → core/workspace-app/actions}/notes.js +0 -0
- /package/{actions → core/workspace-app/actions}/plugins.js +0 -0
- /package/{actions → core/workspace-app/actions}/project-make.js +0 -0
- /package/{actions → core/workspace-app/actions}/row-actions.js +0 -0
- /package/{actions → core/workspace-app/actions}/screens.js +0 -0
- /package/{actions → core/workspace-app/actions}/video-edit.js +0 -0
- /package/{ai-inventory.js → core/workspace-app/ai-inventory.js} +0 -0
- /package/{ai-items.js → core/workspace-app/ai-items.js} +0 -0
- /package/{assistant-cast.js → core/workspace-app/assistant-cast.js} +0 -0
- /package/{assistant-suggestions.js → core/workspace-app/assistant-suggestions.js} +0 -0
- /package/{audio-voice.js → core/workspace-app/audio-voice.js} +0 -0
- /package/{auth-google.js → core/workspace-app/auth-google.js} +0 -0
- /package/{build-ai.js → core/workspace-app/build-ai.js} +0 -0
- /package/{build-order-page.js → core/workspace-app/build-order-page.js} +0 -0
- /package/{build-order.js → core/workspace-app/build-order.js} +0 -0
- /package/{builder-args.js → core/workspace-app/builder-args.js} +0 -0
- /package/{changelog.js → core/workspace-app/changelog.js} +0 -0
- /package/{chat-threads.js → core/workspace-app/chat-threads.js} +0 -0
- /package/{checks.js → core/workspace-app/checks.js} +0 -0
- /package/{claude-args.js → core/workspace-app/claude-args.js} +0 -0
- /package/{claude-sessions.js → core/workspace-app/claude-sessions.js} +0 -0
- /package/{command-author.js → core/workspace-app/command-author.js} +0 -0
- /package/{command-block.js → core/workspace-app/command-block.js} +0 -0
- /package/{commands → core/workspace-app/commands}/defaults.yml +0 -0
- /package/{commands-page.js → core/workspace-app/commands-page.js} +0 -0
- /package/{commands.js → core/workspace-app/commands.js} +0 -0
- /package/{cucumber.js → core/workspace-app/cucumber.js} +0 -0
- /package/{ddev-addons-page.js → core/workspace-app/ddev-addons-page.js} +0 -0
- /package/{ddev-addons.js → core/workspace-app/ddev-addons.js} +0 -0
- /package/{ddev-custom-commands.js → core/workspace-app/ddev-custom-commands.js} +0 -0
- /package/{ddev-domains.js → core/workspace-app/ddev-domains.js} +0 -0
- /package/{ddev-import.js → core/workspace-app/ddev-import.js} +0 -0
- /package/{ddev-mail-page.js → core/workspace-app/ddev-mail-page.js} +0 -0
- /package/{ddev-mail.js → core/workspace-app/ddev-mail.js} +0 -0
- /package/{ddev-options.js → core/workspace-app/ddev-options.js} +0 -0
- /package/{ddev-snapshots.js → core/workspace-app/ddev-snapshots.js} +0 -0
- /package/{ddev.js → core/workspace-app/ddev.js} +0 -0
- /package/{docs → core/workspace-app/docs}/DEVELOPER-GUIDE.md +0 -0
- /package/{docs → core/workspace-app/docs}/README.md +0 -0
- /package/{docs → core/workspace-app/docs}/REFERENCE.md +0 -0
- /package/{docs → core/workspace-app/docs}/USER-GUIDE.md +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-board.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-coffee.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-colours.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-icons.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-moves.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-pick-cookieman.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-settings-voices.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/assistant/avatar-status.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/create-card/create-card.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/create-card/start-ai.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-address.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-off.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-clone-worktree-on.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-ddev-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-domains.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-home.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-ide-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-remove-refusal.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-rows-worktree.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-screens.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-settings.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/guide-workspace-page.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/card-settings.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/drag-reorder.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/home/edit-card.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/claude-code-image.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/edit-with-ai.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/gallery.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/generate-description.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/generate-plan.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/providers.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/svg-form.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/svg-result.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/images/upload.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/2-form.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/8-ddev-options.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/new-folder/files-folder.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/projects/ddev-custom-commands.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/projects/state-chip-stop.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dashboard-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/dev-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/docs-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/ai.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/code.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/docs.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/services.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/icons/sites.svg +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-report-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-scenarios.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/test-steps.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-dark.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/readme/videos-light.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/screens-menu.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/screens-new.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/auth-google-lists.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-1-row.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-2-confirm.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-3-type-name.png +0 -0
- /package/{docs → core/workspace-app/docs}/screenshots/settings/delete-4-type-yes.png +0 -0
- /package/{event-log.js → core/workspace-app/event-log.js} +0 -0
- /package/{files-browser.js → core/workspace-app/files-browser.js} +0 -0
- /package/{folders.js → core/workspace-app/folders.js} +0 -0
- /package/{format.js → core/workspace-app/format.js} +0 -0
- /package/{github-auth.js → core/workspace-app/github-auth.js} +0 -0
- /package/{html.js → core/workspace-app/html.js} +0 -0
- /package/{icons.js → core/workspace-app/icons.js} +0 -0
- /package/{images-ai.js → core/workspace-app/images-ai.js} +0 -0
- /package/{images-create.js → core/workspace-app/images-create.js} +0 -0
- /package/{images-page.js → core/workspace-app/images-page.js} +0 -0
- /package/{images-records.js → core/workspace-app/images-records.js} +0 -0
- /package/{images-render.js → core/workspace-app/images-render.js} +0 -0
- /package/{images-svg-core.js → core/workspace-app/images-svg-core.js} +0 -0
- /package/{images-svg-sanitize.js → core/workspace-app/images-svg-sanitize.js} +0 -0
- /package/{images-svg.js → core/workspace-app/images-svg.js} +0 -0
- /package/{images.js → core/workspace-app/images.js} +0 -0
- /package/{jobs.js → core/workspace-app/jobs.js} +0 -0
- /package/{linger.js → core/workspace-app/linger.js} +0 -0
- /package/{lists.js → core/workspace-app/lists.js} +0 -0
- /package/{media-info.js → core/workspace-app/media-info.js} +0 -0
- /package/{navigation-page.js → core/workspace-app/navigation-page.js} +0 -0
- /package/{navigation.js → core/workspace-app/navigation.js} +0 -0
- /package/{notes.js → core/workspace-app/notes.js} +0 -0
- /package/{palette-index.js → core/workspace-app/palette-index.js} +0 -0
- /package/{playwright.config.js → core/workspace-app/playwright.config.js} +0 -0
- /package/{plugin-install.js → core/workspace-app/plugin-install.js} +0 -0
- /package/{plugin-loader.js → core/workspace-app/plugin-loader.js} +0 -0
- /package/{plugin-manifest.js → core/workspace-app/plugin-manifest.js} +0 -0
- /package/{plugin-settings.js → core/workspace-app/plugin-settings.js} +0 -0
- /package/{plugins → core/workspace-app/plugins}/workspace-notes/index.js +0 -0
- /package/{project-make.js → core/workspace-app/project-make.js} +0 -0
- /package/{project-name.js → core/workspace-app/project-name.js} +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar-moves.js +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar-ui.js +0 -0
- /package/{public → core/workspace-app/public}/assistant-avatar.js +0 -0
- /package/{public → core/workspace-app/public}/avatar/druplicon.vrm +0 -0
- /package/{public → core/workspace-app/public}/css/base.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/ai-assistant.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/assistant-avatar.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/assistant-views.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/build-order.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/clone-as-a-git-worktree.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/command-palette.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/commands-center.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/confirm-dialog.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/create-card.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/ddev-menu.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/deep-chat.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/density.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/floating-assistant-workspace-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/folders.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/full-height-left-assistant-sidebar-all-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/full-height-right-actions-rail-pages-that-have-actions.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/grid-and-list.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/help-page.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/hero.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/images.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/layout-rhythm-and-spacing-polish.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/mic-on-any-field-that-wants-typing.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/modal-fit.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/page-width.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/plugins-page.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/project-cards.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/project-rows.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/reordering-the-workspace-cards.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/responsive-home-and-the-workspace-pages.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/running-commands-as-toasts.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/settings-editor-core-config-yml.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/short-results-as-a-toast-at-the-top.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/sound-player.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/suggest-agents-skills-commands.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/tab-modal.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/terminals.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/test-report-media-one-file-per-row-paged.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/top-toolbar-logo-breadcrumb.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/video-edit.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/web-ide-open-with.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/working-indicators.css +0 -0
- /package/{public → core/workspace-app/public}/css/components/workspace-cards.css +0 -0
- /package/{public → core/workspace-app/public}/favicon.ico +0 -0
- /package/{public → core/workspace-app/public}/favicon.png +0 -0
- /package/{public → core/workspace-app/public}/htmx.min.js +0 -0
- /package/{public → core/workspace-app/public}/logo.png +0 -0
- /package/{public → core/workspace-app/public}/media-player.js +0 -0
- /package/{public → core/workspace-app/public}/mic.js +0 -0
- /package/{public → core/workspace-app/public}/narration.js +0 -0
- /package/{public → core/workspace-app/public}/palette.js +0 -0
- /package/{public → core/workspace-app/public}/sortable.js +0 -0
- /package/{public → core/workspace-app/public}/suggest.js +0 -0
- /package/{public → core/workspace-app/public}/terminal.js +0 -0
- /package/{public → core/workspace-app/public}/themes/default/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/ember/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/press/theme.css +0 -0
- /package/{public → core/workspace-app/public}/themes/slate/theme.css +0 -0
- /package/{public → core/workspace-app/public}/touch-icon.png +0 -0
- /package/{public → core/workspace-app/public}/ui.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ace.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ext-language_tools.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/ext-searchbox.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-css.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-html.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-javascript.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-markdown.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-sh.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/mode-yaml.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/theme-textmate.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/theme-tomorrow_night.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/ace/worker-yaml.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/deep-chat.LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/deep-chat.bundle.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/dm-serif-text-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-italic.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/figtree-latin-600-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/fonts/ubuntu-mono-latin-wght-normal.woff2 +0 -0
- /package/{public → core/workspace-app/public}/vendor/tabler-LICENSE.txt +0 -0
- /package/{public → core/workspace-app/public}/vendor/tabler-sprite.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/loaders/GLTFLoader.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/utils/BufferGeometryUtils.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/addons/utils/SkeletonUtils.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/three.core.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three/three.module.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/three-vrm/LICENSE +0 -0
- /package/{public → core/workspace-app/public}/vendor/three-vrm/three-vrm.module.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit-icons.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit.min.css +0 -0
- /package/{public → core/workspace-app/public}/vendor/uikit.min.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/bookmark-outlined.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/headset.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/menu.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/pause.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/play.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/share.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-off.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/sound-on.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/webship-icons/ui/stop.svg +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-fit.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-unicode11.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/addon-web-links.js +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/xterm.css +0 -0
- /package/{public → core/workspace-app/public}/vendor/xterm/xterm.js +0 -0
- /package/{rag.js → core/workspace-app/rag.js} +0 -0
- /package/{reader.js → core/workspace-app/reader.js} +0 -0
- /package/{repo-blobs.js → core/workspace-app/repo-blobs.js} +0 -0
- /package/{report-pdf.js → core/workspace-app/report-pdf.js} +0 -0
- /package/{row-layout.js → core/workspace-app/row-layout.js} +0 -0
- /package/{screenrc → core/workspace-app/screenrc} +0 -0
- /package/{screens.js → core/workspace-app/screens.js} +0 -0
- /package/{scripts → core/workspace-app/scripts}/code-server-start.sh +0 -0
- /package/{scripts → core/workspace-app/scripts}/images-claude-finish.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-host.sh +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-runner.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/screen-then.js +0 -0
- /package/{scripts → core/workspace-app/scripts}/update.sh +0 -0
- /package/{settings-file.js → core/workspace-app/settings-file.js} +0 -0
- /package/{speech.js → core/workspace-app/speech.js} +0 -0
- /package/{tab-modal.js → core/workspace-app/tab-modal.js} +0 -0
- /package/{test-reports.js → core/workspace-app/test-reports.js} +0 -0
- /package/{themes.js → core/workspace-app/themes.js} +0 -0
- /package/{transcripts.js → core/workspace-app/transcripts.js} +0 -0
- /package/{urls.js → core/workspace-app/urls.js} +0 -0
- /package/{validate.js → core/workspace-app/validate.js} +0 -0
- /package/{version-check.js → core/workspace-app/version-check.js} +0 -0
- /package/{video-edit.js → core/workspace-app/video-edit.js} +0 -0
- /package/{waiting.js → core/workspace-app/waiting.js} +0 -0
- /package/{workspace-card-tools.js → core/workspace-app/workspace-card-tools.js} +0 -0
- /package/{workspace-delete.js → core/workspace-app/workspace-delete.js} +0 -0
- /package/{workspace-types.js → core/workspace-app/workspace-types.js} +0 -0
- /package/{workspaces.js → core/workspace-app/workspaces.js} +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const test = require('node:test');
|
|
2
|
+
const assert = require('node:assert/strict');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const root = path.join(__dirname, '..', '..');
|
|
7
|
+
const read = (...p) => fs.readFileSync(path.join(root, ...p), 'utf8');
|
|
8
|
+
|
|
9
|
+
test('exit types /exit and then Enter, only into a live Claude Code session, and only with confirm', () => {
|
|
10
|
+
const src = read('actions', 'claude-session.js');
|
|
11
|
+
assert.match(src, /if \(form\.confirm !== 'yes'\)/);
|
|
12
|
+
assert.match(src, /if \(!\(await isClaudeSession\(session\)\)\)/);
|
|
13
|
+
assert.match(src, /\['-S', session, '-X', 'stuff', '\\x1b'\]/);
|
|
14
|
+
assert.match(src, /\['-S', session, '-X', 'stuff', '\/exit'\]/);
|
|
15
|
+
assert.match(src, /\['-S', session, '-X', 'stuff', '\\r'\]/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('resume starts claude --resume with the newest conversation in that folder, never an id from the request', () => {
|
|
19
|
+
const src = read('actions', 'claude-session.js');
|
|
20
|
+
assert.match(src, /claudeSessions\.sessionsFor\(dir, \{ limit: 1, resumableOnly: true \}\)/);
|
|
21
|
+
assert.match(src, /cmd: `exec claude --resume \$\{last\.id\}`/);
|
|
22
|
+
assert.match(src, /claudeSessions\.isSessionId\(last\.id\)/);
|
|
23
|
+
assert.doesNotMatch(src, /form\.(id|resume)/);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('the SCREENS menu offers Exit for each live Claude session, and Resume when none runs', () => {
|
|
27
|
+
const { menuItems } = require('../../actions/claude-session');
|
|
28
|
+
const agents = new Set(['dev--p1--claude']);
|
|
29
|
+
const html = menuItems({ ws: 'dev', project: 'p1', sessions: [{ session: 'dev--p1--claude', state: 'Detached' }, { session: 'dev--p1--shell', state: 'Detached' }], agents, dir: '' });
|
|
30
|
+
assert.match(html, /class="arm-step" title="Exit Claude Code in dev--p1--claude \(\/exit\)"/);
|
|
31
|
+
assert.equal((html.match(/claude-exit/g) || []).length, 1);
|
|
32
|
+
assert.doesNotMatch(html, /claude-resume/);
|
|
33
|
+
assert.equal(menuItems({ ws: 'dev', project: 'p1', sessions: [], agents: new Set(), dir: '' }), '');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('the Screens page, the card menu and the actions are wired in', () => {
|
|
37
|
+
assert.match(read('server.js'), /\.\.\.require\('\.\/actions\/claude-session'\)/);
|
|
38
|
+
assert.match(read('server.js'), /require\('\.\/actions\/claude-session'\)\.menuItems\(/);
|
|
39
|
+
assert.match(read('screens.js'), /claudeHere\.has\(s\.session\) && !dead \?/);
|
|
40
|
+
assert.match(read('actions', 'screens.js'), /\n onHostScreen,\n/);
|
|
41
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commandFlags feeds two surfaces that must not lie: the AI context's command
|
|
3
|
+
* contract and the Run-a-tool flag hints. It resolves the args/arg-*.sh a
|
|
4
|
+
* command sources — including through the `arg-${distribution_name}.sh`
|
|
5
|
+
* indirection every builder uses — so these tests pin both paths against the
|
|
6
|
+
* real shipped files.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { test } = require('node:test');
|
|
10
|
+
const assert = require('node:assert');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const { commandFlags, ROOT } = require('../../workspaces');
|
|
14
|
+
|
|
15
|
+
const dev = path.join(ROOT, 'dev');
|
|
16
|
+
|
|
17
|
+
test('a builder with a variable arg file resolves to the real contract', () => {
|
|
18
|
+
const flags = commandFlags(dev, 'cmd-drupal11-recommended-project.sh').map((f) => f.long);
|
|
19
|
+
for (const expected of ['--install', '--launch', '--dev']) {
|
|
20
|
+
assert.ok(flags.includes(expected), `${expected} in ${flags}`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('a site-template builder reports no flags, because its parser takes a name and nothing else', () => {
|
|
25
|
+
// arg-site-template.sh declares only PROJECT_NAME. A hint offering --install here would be a
|
|
26
|
+
// hint for a flag the build exits on.
|
|
27
|
+
const templates = path.join(ROOT, 'site-templates');
|
|
28
|
+
assert.deepStrictEqual(commandFlags(templates, 'cmd-byte-project.sh'), []);
|
|
29
|
+
assert.deepStrictEqual(commandFlags(templates, 'cmd-website_starter-project.sh'), []);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('a distribution builder reports its opt-in install, with the help text that travels with it', () => {
|
|
33
|
+
const flags = commandFlags(dev, 'cmd-webship11-0-x-project.sh');
|
|
34
|
+
const longs = flags.map((f) => f.long);
|
|
35
|
+
assert.ok(longs.includes('--install'));
|
|
36
|
+
assert.ok(longs.includes('--add-users'));
|
|
37
|
+
// The help text travels with the flag — that is what the hints show.
|
|
38
|
+
const install = flags.find((f) => f.long === '--install');
|
|
39
|
+
assert.ok(install.short === '-i');
|
|
40
|
+
assert.ok(install.help.length > 10);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('a script with no arg file yields an empty contract, not a throw', () => {
|
|
44
|
+
assert.deepStrictEqual(commandFlags(dev, 'no-such-script.sh'), []);
|
|
45
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command palette, the shortcut sheet and the go-to chords.
|
|
3
|
+
*
|
|
4
|
+
* Nothing in the dashboard answered a keyboard: every move was a pointer to a card or a menu. Ctrl or Cmd + K
|
|
5
|
+
* (and "/") opens one list of workspaces, pages, projects and a few actions; "?" is the sheet; "g" then a letter
|
|
6
|
+
* goes to a workspace. None of it fires while somebody types in a field, and the page behind is out of reach
|
|
7
|
+
* while the palette is open.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { test, before, after } = require('node:test');
|
|
11
|
+
const assert = require('node:assert');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const APP = path.join(__dirname, '../..');
|
|
17
|
+
const REPO = path.join(APP, '../..');
|
|
18
|
+
const { copyConfig } = require('./_isolated-config');
|
|
19
|
+
const read = (f) => fs.readFileSync(path.join(APP, f), 'utf8');
|
|
20
|
+
|
|
21
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ws-palette-root-'));
|
|
22
|
+
process.env.WORKSPACE_ROOT = root;
|
|
23
|
+
const { buildIndex, PAGES } = require('../../palette-index');
|
|
24
|
+
|
|
25
|
+
before(() => {
|
|
26
|
+
fs.mkdirSync(path.join(root, 'core'), { recursive: true });
|
|
27
|
+
copyConfig(path.join(REPO, 'core/config'), path.join(root, 'core/config'));
|
|
28
|
+
fs.symlinkSync(path.join(REPO, 'core/scripts'), path.join(root, 'core/scripts'));
|
|
29
|
+
fs.symlinkSync(APP, path.join(root, 'core/workspace-app'));
|
|
30
|
+
fs.mkdirSync(path.join(root, 'dev/site-one/.ddev'), { recursive: true });
|
|
31
|
+
fs.writeFileSync(path.join(root, 'dev/site-one/.ddev/config.yaml'), 'name: site-one\n');
|
|
32
|
+
fs.mkdirSync(path.join(root, 'dev/plain-folder'), { recursive: true });
|
|
33
|
+
fs.mkdirSync(path.join(root, 'dev/.hidden'), { recursive: true });
|
|
34
|
+
fs.writeFileSync(path.join(root, 'dev/a-file.md'), 'x');
|
|
35
|
+
fs.mkdirSync(path.join(root, 'videos'), { recursive: true });
|
|
36
|
+
});
|
|
37
|
+
after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
38
|
+
|
|
39
|
+
test('the index lists workspaces, the pages, and the project folders of workspaces that hold projects', () => {
|
|
40
|
+
const idx = buildIndex();
|
|
41
|
+
assert.ok(idx.workspaces.some((w) => w.key === 'dev' && /^https:\/\/dev\./.test(w.url)));
|
|
42
|
+
assert.deepStrictEqual(idx.pages.map((p) => p.url), PAGES.map((p) => p.url));
|
|
43
|
+
const names = idx.projects.filter((p) => p.ws === 'dev').map((p) => p.name).sort();
|
|
44
|
+
assert.deepStrictEqual(names, ['plain-folder', 'site-one'], 'folders only: no file, no hidden folder');
|
|
45
|
+
const site = idx.projects.find((p) => p.name === 'site-one');
|
|
46
|
+
assert.strictEqual(site.ddev, true);
|
|
47
|
+
assert.match(site.url, /^https:\/\/site-one\.dev\./, 'a DDEV project opens its site');
|
|
48
|
+
assert.match(idx.projects.find((p) => p.name === 'plain-folder').url, /^https:\/\/dev\.[^/]+\/$/, 'a folder opens its workspace');
|
|
49
|
+
assert.ok(!idx.projects.some((p) => p.ws === 'videos'), 'a files workspace has no projects to list');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('it opens on Ctrl or Cmd + K and "/", the sheet on "?", and chords on "g" then a letter', () => {
|
|
53
|
+
const js = read('public/palette.js');
|
|
54
|
+
assert.match(js, /\(e\.ctrlKey \|\| e\.metaKey\) && !e\.altKey && !e\.shiftKey && k\.toLowerCase\(\) === 'k'/);
|
|
55
|
+
assert.match(js, /if \(k === '\/'\) \{ e\.preventDefault\(\); open\(''\); return; \}/);
|
|
56
|
+
assert.match(js, /if \(k === '\?'\) \{ e\.preventDefault\(\); open\('\?'\); return; \}/);
|
|
57
|
+
assert.match(js, /if \(k === 'g'\) \{ chord = Date\.now\(\); \}/);
|
|
58
|
+
assert.match(js, /Date\.now\(\) - chord < 1200/, 'the second key has to come within a moment');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('it never fires while somebody is typing, or while a modal is open', () => {
|
|
62
|
+
const js = read('public/palette.js');
|
|
63
|
+
assert.match(js, /n === 'input' \|\| n === 'textarea' \|\| n === 'select' \|\| t\.isContentEditable/);
|
|
64
|
+
assert.match(js, /\.ace_editor, \.xterm/, 'an editor and a terminal are somewhere to type');
|
|
65
|
+
assert.match(js, /if \(typing\(e\.target\) \|\| modalOpen\(\)\) return;/);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('it is a dialog with a combobox and a listbox, announces its results, and holds the page behind it inert', () => {
|
|
69
|
+
const js = read('public/palette.js');
|
|
70
|
+
assert.match(js, /role="dialog" aria-modal="true" aria-label="Command palette"/);
|
|
71
|
+
assert.match(js, /role="combobox" aria-expanded="true" aria-controls="palette-list" aria-autocomplete="list"/);
|
|
72
|
+
assert.match(js, /role="listbox" aria-label="Results"/);
|
|
73
|
+
assert.match(js, /setAttribute\('role', 'option'\)/);
|
|
74
|
+
assert.match(js, /aria-activedescendant/);
|
|
75
|
+
assert.match(js, /aria-live="polite"/);
|
|
76
|
+
assert.match(js, /el\.inert = true/);
|
|
77
|
+
assert.match(js, /returnTo\.focus\(\)/, 'focus goes back to where it was');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('it is served on every page, from one small JSON route', () => {
|
|
81
|
+
const server = read('server.js');
|
|
82
|
+
assert.match(server, /<script src="\$\{assetUrl\('\/palette\.js'\)\}" defer><\/script>/);
|
|
83
|
+
assert.match(server, /pathname === '\/fragments\/palette\.json'/);
|
|
84
|
+
assert.match(server, /'Cache-Control': 'no-store'/);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('the stylesheet is a component with a visible selected row and honours reduced motion', () => {
|
|
88
|
+
const css = read('public/css/components/command-palette.css');
|
|
89
|
+
assert.match(css, /\.palette-row\[aria-selected="true"\] \{ background: var\(--subtle-brand\);/);
|
|
90
|
+
assert.match(css, /prefers-reduced-motion: reduce/);
|
|
91
|
+
assert.match(css, /#palette\[hidden\] \{ display: none; \}/);
|
|
92
|
+
assert.match(require('../../themes').assembleCss(''), /\/\* component: command-palette \*\//);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('there is a visible way in: a toolbar button that says what key opens it', () => {
|
|
96
|
+
const server = read('server.js');
|
|
97
|
+
assert.match(server, /class="uk-navbar-item palette-trigger" type="button"/);
|
|
98
|
+
assert.match(server, /aria-label="Open the command palette" aria-keyshortcuts="Control\+K Meta\+K"/);
|
|
99
|
+
assert.match(read('public/palette.js'), /closest\('\.palette-trigger'\)/);
|
|
100
|
+
assert.match(read('public/palette.js'), /k\.textContent = '⌘K'/);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('letters-in-order matches are a fallback, not noise next to real matches', () => {
|
|
104
|
+
assert.match(read('public/palette.js'), /out\.some\(function \(x\) \{ return x\.score >= 40; \}\)\) out = out\.filter\(function \(x\) \{ return x\.score >= 40; \}\)/);
|
|
105
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command catalog and the Commands page: commands as data, what each runs on, and the page out of Settings.
|
|
3
|
+
*
|
|
4
|
+
* What must never drift: every built-in command is valid; a line never carries $ or a backtick, and a value reaches it
|
|
5
|
+
* only single-quoted and only where its target gives one; a target stays inside its workspace or project; yours replace
|
|
6
|
+
* a built-in one by id, and a broken file of yours leaves the built-in ones; a write never overwrites a newer file; and
|
|
7
|
+
* Commands is its own page, with Settings → Commands and Row actions sending you to it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const assert = require('node:assert');
|
|
11
|
+
const { test } = require('node:test');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'commands-'));
|
|
17
|
+
process.env.COMMANDS_FILE = path.join(tmp, 'commands.yml');
|
|
18
|
+
process.env.ROW_ACTIONS_FILE = path.join(tmp, 'row-actions.yml');
|
|
19
|
+
process.env.ROW_ACTIONS_SEEN_FILE = path.join(tmp, 'row-actions.seen.json');
|
|
20
|
+
process.env.ROW_ACTIONS_HISTORY_FILE = path.join(tmp, 'row-actions.history.jsonl');
|
|
21
|
+
process.on('exit', () => fs.rmSync(tmp, { recursive: true, force: true }));
|
|
22
|
+
|
|
23
|
+
const cc = require('../../commands');
|
|
24
|
+
|
|
25
|
+
const APP = path.join(__dirname, '..', '..');
|
|
26
|
+
const cmd = (over = {}) => cc.cleanCommand({ id: 'cr', label: 'Rebuild caches', on: 'project', run: 'ddev drush cr', ...over });
|
|
27
|
+
const refusal = (over) => cc.commandRefusal(cmd(over));
|
|
28
|
+
const shipped = () => { delete process.env.COMMANDS_DEFAULTS_FILE; cc.invalidateCatalog(); };
|
|
29
|
+
|
|
30
|
+
test('every built-in command is valid, has its own id, and every kind of target has some', () => {
|
|
31
|
+
shipped();
|
|
32
|
+
fs.rmSync(process.env.COMMANDS_FILE, { force: true });
|
|
33
|
+
const cat = cc.loadCatalog();
|
|
34
|
+
assert.strictEqual(cat.builtinError, '');
|
|
35
|
+
assert.deepStrictEqual(cat.problems, []);
|
|
36
|
+
assert.ok(cat.builtin.length >= 10);
|
|
37
|
+
assert.strictEqual(new Set(cat.builtin.map((e) => e.id)).size, cat.builtin.length);
|
|
38
|
+
for (const on of Object.keys(cc.ON)) assert.ok(cat.builtin.some((e) => e.on === on), `no built-in command runs on ${on}`);
|
|
39
|
+
// DDEV only: no built-in command runs composer or drush on the host.
|
|
40
|
+
for (const e of cat.builtin) assert.doesNotMatch(cc.lineOf(e), /(^|[;&|]\s*)(composer|drush) /, e.id);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('a line never carries $ or a backtick, and a value only where its target gives one', () => {
|
|
44
|
+
assert.strictEqual(refusal({}), '');
|
|
45
|
+
assert.match(refusal({ run: 'echo $HOME' }), /\$ or a backtick/);
|
|
46
|
+
assert.match(refusal({ run: 'echo `id`' }), /\$ or a backtick/);
|
|
47
|
+
assert.match(refusal({ on: 'workspace', run: 'ls {{project}}' }), /\{\{project\}\} needs "on" to be project or folder or file/);
|
|
48
|
+
assert.match(refusal({ on: 'project', run: 'head {{file}}' }), /needs "on" to be file/);
|
|
49
|
+
assert.match(refusal({ run: 'echo {{home}}' }), /is not a value a command can use/);
|
|
50
|
+
assert.match(refusal({ run: 'git switch {{opt.branch}}' }), /no option called branch/);
|
|
51
|
+
assert.strictEqual(refusal({ run: 'git switch {{opt.branch}}', options: [{ name: 'branch', required: true }] }), '');
|
|
52
|
+
assert.match(refusal({ script: 'dev/cmd-tools-backup-dev.sh' }), /either run .* and not both/);
|
|
53
|
+
assert.match(refusal({ run: '', script: 'dev/../cmd-x.sh' }), /script is <workspace>\/cmd-<name>\.sh/);
|
|
54
|
+
assert.match(refusal({ args: '{{project}}' }), /args go with a script/);
|
|
55
|
+
assert.match(refusal({ on: 'everywhere' }), /"on" is workspace, project, folder, file/);
|
|
56
|
+
assert.match(refusal({ id: 'Bad Id' }), /the id is lower-case/);
|
|
57
|
+
assert.match(refusal({ label: 'قل مرحبا' }), /the label is letters/);
|
|
58
|
+
assert.strictEqual(refusal({ on: 'file', run: 'head -n 5 -- {{file}}' }), '');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('yours replace a built-in one by id, in its place; a broken file of yours leaves the built-in ones', () => {
|
|
62
|
+
const defaults = path.join(tmp, 'defaults.yml');
|
|
63
|
+
fs.writeFileSync(defaults, 'commands:\n - { id: one, label: One, on: project, run: echo one }\n - { id: two, label: Two, on: project, run: echo two }\n');
|
|
64
|
+
process.env.COMMANDS_DEFAULTS_FILE = defaults;
|
|
65
|
+
fs.writeFileSync(process.env.COMMANDS_FILE, 'commands:\n'
|
|
66
|
+
+ ' - { id: one, label: My one, on: project, run: echo mine }\n'
|
|
67
|
+
+ ' - { id: three, label: Three, on: file, run: "head {{file}}" }\n'
|
|
68
|
+
+ ' - { id: bad, label: Bad, on: project, run: echo $HOME }\n');
|
|
69
|
+
cc.invalidateCatalog();
|
|
70
|
+
let cat = cc.loadCatalog();
|
|
71
|
+
assert.deepStrictEqual(cat.commands.map((e) => [e.id, e.source, !!e.overrides]), [['one', 'yours', true], ['two', 'builtin', false], ['three', 'yours', false]]);
|
|
72
|
+
assert.strictEqual(cat.commands[0].label, 'My one');
|
|
73
|
+
assert.match(cat.problems.join(' '), /bad: .*\$ or a backtick/);
|
|
74
|
+
|
|
75
|
+
fs.writeFileSync(process.env.COMMANDS_FILE, 'commands: [\n');
|
|
76
|
+
cc.invalidateCatalog();
|
|
77
|
+
cat = cc.loadCatalog();
|
|
78
|
+
assert.match(cat.error, /not valid YAML/);
|
|
79
|
+
assert.deepStrictEqual(cat.commands.map((e) => e.id), ['one', 'two']);
|
|
80
|
+
shipped();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('a write never overwrites a newer file, and no commands at all removes the file', () => {
|
|
84
|
+
fs.rmSync(process.env.COMMANDS_FILE, { force: true });
|
|
85
|
+
cc.invalidateCatalog();
|
|
86
|
+
cc.saveYourCommands([cmd()], '');
|
|
87
|
+
assert.match(fs.readFileSync(process.env.COMMANDS_FILE, 'utf8'), /run: ddev drush cr/);
|
|
88
|
+
assert.throws(() => cc.saveYourCommands([cmd({ label: 'Other' })], '1'), (err) => err.code === 'CHANGED');
|
|
89
|
+
cc.saveYourCommands([], String(Math.floor(fs.statSync(process.env.COMMANDS_FILE).mtimeMs)));
|
|
90
|
+
assert.ok(!fs.existsSync(process.env.COMMANDS_FILE));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Definition refuses an empty paste and a YAML alias bomb, and says what would change', () => {
|
|
94
|
+
shipped();
|
|
95
|
+
assert.match(cc.checkDefinition('').errors.join(' '), /empty/);
|
|
96
|
+
const levels = ['a: &a [x, x, x, x, x, x, x, x, x]'];
|
|
97
|
+
for (const [i, k] of ['b', 'c', 'd', 'e'].entries()) levels.push(`${k}: &${k} [${Array(9).fill(`*${'abcd'[i]}`).join(', ')}]`);
|
|
98
|
+
const started = Date.now();
|
|
99
|
+
assert.match(cc.checkDefinition([...levels, 'commands: []'].join('\n')).errors.join(' '), /too large or too deeply nested/);
|
|
100
|
+
assert.ok(Date.now() - started < 2000);
|
|
101
|
+
assert.match(cc.checkDefinition('commands:\n - { id: x, label: X, on: project, run: echo $X }\n').errors.join(' '), /Command 1 \(x\): .*\$ or a backtick/);
|
|
102
|
+
const r = cc.checkDefinition('commands:\n - { id: ddev-restart, label: Restart it, on: project, when: [ddev], run: ddev restart }\n - { id: mine, label: Mine, on: workspace, run: ls }\n');
|
|
103
|
+
assert.deepStrictEqual(r.errors, []);
|
|
104
|
+
assert.match(r.notes.join(' '), /Adds ddev-restart, mine\./);
|
|
105
|
+
assert.match(r.notes.join(' '), /Your version of the built-in ddev-restart\./);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('a target stays inside its workspace or project, and gives only the values it has', () => {
|
|
109
|
+
const wsDir = path.join(tmp, 'ws1');
|
|
110
|
+
const site = path.join(wsDir, 'site');
|
|
111
|
+
fs.mkdirSync(path.join(site, 'web', 'modules'), { recursive: true });
|
|
112
|
+
fs.writeFileSync(path.join(site, 'composer.json'), '{}');
|
|
113
|
+
fs.writeFileSync(path.join(wsDir, 'notes.md'), '# notes');
|
|
114
|
+
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'commands-outside-'));
|
|
115
|
+
process.on('exit', () => fs.rmSync(outside, { recursive: true, force: true }));
|
|
116
|
+
fs.writeFileSync(path.join(outside, 'secret'), 'x');
|
|
117
|
+
fs.symlinkSync(outside, path.join(site, 'out'));
|
|
118
|
+
const env = { isValidWorkspace: (k) => k === 'ws1', workspaceDir: () => wsDir };
|
|
119
|
+
const at = (on, form) => cc.resolveTarget(cmd({ on, run: 'ls' }), { target_ws: 'ws1', ...form }, env);
|
|
120
|
+
|
|
121
|
+
assert.match(at('project', { target_ws: 'nope' }).error, /Pick a workspace/);
|
|
122
|
+
assert.match(at('project', {}).error, /Pick a project/);
|
|
123
|
+
assert.match(at('project', { target_project: '../etc' }).error, /not a project name/);
|
|
124
|
+
assert.match(at('file', { target_project: 'site', target_path: '../../etc/passwd' }).error, /relative path inside the target/);
|
|
125
|
+
assert.match(at('file', { target_project: 'site', target_path: 'out/secret' }).error, /leads outside site/);
|
|
126
|
+
assert.match(at('file', { target_project: 'site', target_path: 'a$b' }).error, /relative path inside the target/);
|
|
127
|
+
assert.match(at('folder', { target_project: 'site', target_path: 'composer.json' }).error, /is not a folder/);
|
|
128
|
+
assert.match(at('file', { target_project: 'site', target_path: 'web' }).error, /is not a file/);
|
|
129
|
+
assert.match(at('file', { target_project: 'site' }).error, /Name the file/);
|
|
130
|
+
|
|
131
|
+
let t = at('file', { target_project: 'site', target_path: './composer.json' });
|
|
132
|
+
assert.deepStrictEqual([t.cwd, t.vars.file, t.vars.folder, t.vars.project, t.where], [site, 'composer.json', '.', 'site', 'site/composer.json']);
|
|
133
|
+
t = at('folder', { target_project: 'site', target_path: 'web/modules/' });
|
|
134
|
+
assert.deepStrictEqual([t.cwd, t.vars.folder, t.vars.file], [path.join(site, 'web', 'modules'), 'web/modules', '']);
|
|
135
|
+
t = at('file', { target_path: 'notes.md' });
|
|
136
|
+
assert.deepStrictEqual([t.cwd, t.vars.project, t.where, t.name], [wsDir, '', 'notes.md', 'ws1/notes.md']);
|
|
137
|
+
t = at('workspace', { target_project: 'site' });
|
|
138
|
+
assert.deepStrictEqual([t.cwd, t.vars.project, t.name], [wsDir, '', 'ws1']);
|
|
139
|
+
|
|
140
|
+
const suggest = (on, typed) => cc.pathSuggestions(cmd({ on }), { target_ws: 'ws1', target_project: 'site', target_path: typed }, env);
|
|
141
|
+
assert.deepStrictEqual(suggest('folder', 'web/'), ['web/modules/']);
|
|
142
|
+
assert.deepStrictEqual(suggest('file', 'out/'), [], 'a link out of the project offers nothing');
|
|
143
|
+
assert.deepStrictEqual(suggest('file', '../'), []);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('the line quotes every value, and prints the markers a run is read back from', () => {
|
|
147
|
+
const line = cc.commandLine(cmd({ on: 'file', run: 'head -n 5 -- {{file}}' }), { file: "it's here.txt" });
|
|
148
|
+
assert.strictEqual(line, `{ head -n 5 -- 'it'\\''s here.txt' ; } && echo "ROW-ACTION succeeded" || echo "ROW-ACTION failed"`);
|
|
149
|
+
const script = cc.commandLine(cmd({ run: '', script: 'dev/cmd-tools-backup-dev.sh', args: '{{project}} --keep 2' }), { project: 'site' }, {}, { workspaceDir: () => '/w/dev' });
|
|
150
|
+
assert.match(script, /^\{ bash '\/w\/dev\/cmd-tools-backup-dev\.sh' 'site' --keep 2 ; \}/);
|
|
151
|
+
const opt = cc.commandLine(cmd({ run: 'git switch {{opt.branch}}', options: [{ name: 'branch' }] }), {}, { branch: 'feature/x' });
|
|
152
|
+
assert.match(opt, /git switch 'feature\/x'/);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('the list shows built-in, yours and scripts together, and filters by source and by what it runs on', () => {
|
|
156
|
+
shipped();
|
|
157
|
+
const cp = require('../../commands-page');
|
|
158
|
+
const scripts = [{ key: 'dev', label: 'Dev', items: [{ file: 'cmd-tools-backup-dev.sh', title: 'Back up', desc: 'Backs up a project' }] }];
|
|
159
|
+
let html = cp.catalogListHtml({ per: 'all', q: '' }, { scripts });
|
|
160
|
+
assert.match(html, /Scripts · Dev/);
|
|
161
|
+
assert.match(html, /href="\/commands\/c\/ddev-restart"/);
|
|
162
|
+
assert.match(html, /href="\/commands\/s\/dev\/cmd-tools-backup-dev\.sh"/);
|
|
163
|
+
html = cp.catalogListHtml({ per: 'all', q: '', source: 'script' }, { scripts });
|
|
164
|
+
assert.doesNotMatch(html, /\/commands\/c\//);
|
|
165
|
+
html = cp.catalogListHtml({ per: 'all', q: '', on: 'file' }, { scripts });
|
|
166
|
+
assert.match(html, /\/commands\/c\/file-head/);
|
|
167
|
+
assert.doesNotMatch(html, /ddev-restart/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('the handlers: add, refuse and keep what was typed, change a built-in, copy, delete, run by id only', async () => {
|
|
171
|
+
shipped();
|
|
172
|
+
fs.rmSync(process.env.COMMANDS_FILE, { force: true });
|
|
173
|
+
cc.invalidateCatalog();
|
|
174
|
+
const actions = require('../../actions/commands-center');
|
|
175
|
+
const call = async (route, form) => {
|
|
176
|
+
const out = {};
|
|
177
|
+
await actions[route]({ form, workspace: 'dev', pathname: route, send: (html, code = 200) => { out.html = html; out.code = code; } });
|
|
178
|
+
return out;
|
|
179
|
+
};
|
|
180
|
+
const mtime = () => String(cc.loadCatalog().mtime);
|
|
181
|
+
|
|
182
|
+
let r = await call('/actions/cc-save', { cc_label: 'Say hi', cc_on: 'project', cc_kind: 'run', cc_run: 'echo $HOME', mtime: mtime() });
|
|
183
|
+
assert.strictEqual(r.code, 400);
|
|
184
|
+
assert.match(r.html, /\$ or a backtick/);
|
|
185
|
+
assert.match(r.html, /name="cc_run"[^>]*>echo \$HOME<\/textarea>/, 'a refused form keeps what was typed');
|
|
186
|
+
r = await call('/actions/cc-save', { cc_label: 'Say hi', cc_on: 'folder', cc_kind: 'run', cc_run: 'ls -la', cc_when: 'git', cc_tags: 'files, look', mtime: mtime() });
|
|
187
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
188
|
+
const e = cc.findCommand('say-hi');
|
|
189
|
+
assert.deepStrictEqual([e.source, e.on, e.when, e.tags], ['yours', 'folder', ['git'], ['files', 'look']]);
|
|
190
|
+
r = await call('/actions/cc-save', { cc_label: 'Again', cc_id: 'say-hi', cc_on: 'project', cc_kind: 'run', cc_run: 'ls', mtime: mtime() });
|
|
191
|
+
assert.strictEqual(r.code, 409);
|
|
192
|
+
r = await call('/actions/cc-save', { cc_label: 'Stale', cc_on: 'project', cc_kind: 'run', cc_run: 'ls', mtime: '1' });
|
|
193
|
+
assert.strictEqual(r.code, 409);
|
|
194
|
+
assert.match(r.html, /changed since this page was loaded/);
|
|
195
|
+
|
|
196
|
+
r = await call('/actions/cc-save', { id: 'ddev-restart', cc_label: 'Restart', cc_on: 'project', cc_when: 'ddev', cc_kind: 'run', cc_run: 'ddev restart --skip-hooks', mtime: mtime() });
|
|
197
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
198
|
+
assert.strictEqual(cc.findCommand('ddev-restart').overrides, true);
|
|
199
|
+
r = await call('/actions/cc-remove', { id: 'ddev-describe', mtime: mtime() });
|
|
200
|
+
assert.strictEqual(r.code, 404, 'a built-in command cannot be deleted');
|
|
201
|
+
r = await call('/actions/cc-remove', { id: 'ddev-restart', mtime: mtime(), from: 'page' });
|
|
202
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
203
|
+
assert.strictEqual(cc.findCommand('ddev-restart').source, 'builtin', 'deleting yours brings the built-in one back');
|
|
204
|
+
|
|
205
|
+
r = await call('/actions/cc-copy', { id: 'git-status', mtime: mtime() });
|
|
206
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
207
|
+
assert.strictEqual(cc.findCommand('git-status-copy').label, 'Git status copy');
|
|
208
|
+
|
|
209
|
+
r = await call('/actions/cc-run', { id: 'no-such-command' });
|
|
210
|
+
assert.strictEqual(r.code, 404);
|
|
211
|
+
r = await call('/actions/cc-run', { id: 'git-status', target_ws: 'no-such-workspace' });
|
|
212
|
+
assert.strictEqual(r.code, 400);
|
|
213
|
+
assert.match(r.html, /Pick a workspace/);
|
|
214
|
+
|
|
215
|
+
r = await call('/actions/cc-definition-save', { definition: 'commands:\n - { id: x, label: X, on: project, run: echo $X }\n', mtime: mtime() });
|
|
216
|
+
assert.strictEqual(r.code, 400);
|
|
217
|
+
assert.match(r.html, /id: x, label: X/, 'a refused definition keeps what was pasted');
|
|
218
|
+
r = await call('/actions/cc-reset', { mtime: mtime() });
|
|
219
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
220
|
+
assert.ok(!fs.existsSync(process.env.COMMANDS_FILE));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('code is edited and shown in Ace: YAML, shell steps, a one-line command, and a script read-only', () => {
|
|
224
|
+
shipped();
|
|
225
|
+
const cp = require('../../commands-page');
|
|
226
|
+
const rl = require('../../row-layout');
|
|
227
|
+
const def = cp.definitionBody();
|
|
228
|
+
assert.match(def, /<textarea[^>]*name="definition"[^>]*data-ace="yaml"/);
|
|
229
|
+
assert.match(def, /readonly[^>]*data-ace="yaml" data-ace-readonly="1" aria-label="The built-in commands"/);
|
|
230
|
+
const page = cp.commandPageBody(cc.findCommand('git-status'), { scripts: [] });
|
|
231
|
+
assert.match(page, /name="cc_run"[^>]*data-ace="sh" data-ace-single="1"/);
|
|
232
|
+
assert.match(page, /name="cc_args"[^>]*data-ace="sh" data-ace-single="1"/);
|
|
233
|
+
assert.match(page, /readonly[^>]*data-ace="yaml" data-ace-readonly="1" aria-label="This command as YAML"/);
|
|
234
|
+
assert.match(cp.scriptPageBody({ ws: 'dev', file: 'cmd-x.sh', source: 'echo hi\n' }), /readonly[^>]*data-ace="sh" data-ace-readonly="1"/);
|
|
235
|
+
const editor = rl.rowActionsEditorHtml();
|
|
236
|
+
assert.match(editor, /name="definition"[^>]*data-ace="yaml"/);
|
|
237
|
+
// The add-a-button fields moved into the modal, and its steps box is Ace too.
|
|
238
|
+
assert.match(rl.addButtonFormHtml(), /name="add_steps_new"[^>]*data-ace="sh"/);
|
|
239
|
+
const ui = fs.readFileSync(path.join(APP, 'public', 'ui.js'), 'utf8');
|
|
240
|
+
assert.match(ui, /const single = ta\.dataset\.aceSingle === '1';/, 'a one-line editor');
|
|
241
|
+
assert.match(ui, /bindKey: \{ win: 'Enter\|Shift-Enter', mac: 'Enter\|Shift-Enter' \}, exec: \(\) => \{\}/, 'Enter adds no line');
|
|
242
|
+
assert.match(ui, /if \(readOnly\) editor\.setOptions\(\{ readOnly: true/, 'a read-only editor');
|
|
243
|
+
assert.match(ui, /new ResizeObserver\(\(\) => editor\.resize\(\)\)\.observe\(host\)/, 'drawn again when a closed section opens');
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test('an action step can use a command: its line, filled for the project, run from the right folder', () => {
|
|
247
|
+
shipped();
|
|
248
|
+
const rl = require('../../row-layout');
|
|
249
|
+
const env = { workspaceDir: (k) => `/w/${k}`, exists: () => true };
|
|
250
|
+
const ctx = { workspace: 'dev', project: 'site', projectDir: '/w/dev/site' };
|
|
251
|
+
const action = (steps, over = {}) => rl.cleanCustom({ id: 'a', label: 'A', button: 'ddev', group: 'g', steps, ...over });
|
|
252
|
+
|
|
253
|
+
assert.strictEqual(rl.customRefusal(action([{ use: 'git-status' }, 'echo done'])), '');
|
|
254
|
+
assert.match(rl.customRefusal(action([{ use: 'Bad Id' }])), /not a command id/);
|
|
255
|
+
assert.match(rl.customRefusal(action([{ use: 'git-status', command: 'ls' }])), /one or the other/);
|
|
256
|
+
assert.match(rl.useRefusal(action(['echo a', { use: 'no-such-command' }])), /step 2 uses no-such-command, which is not a command/);
|
|
257
|
+
assert.match(rl.useRefusal(action([{ use: 'file-head' }])), /runs on a file/);
|
|
258
|
+
|
|
259
|
+
assert.deepStrictEqual(rl.customToDoc(action([{ use: 'git-status' }])).steps, [{ use: 'git-status' }]);
|
|
260
|
+
const round = rl.cleanCustom(rl.customToDoc(action(['echo a', { use: 'drush-status', on_error: 'echo failed' }])));
|
|
261
|
+
assert.deepStrictEqual(round.steps.map(rl.stepText), ['echo a', 'use: drush-status']);
|
|
262
|
+
assert.strictEqual(round.steps[1].onError, 'echo failed');
|
|
263
|
+
|
|
264
|
+
let x = cc.expandSteps(action([{ use: 'git-status' }, 'echo done']), ctx, env);
|
|
265
|
+
assert.deepStrictEqual(x.entry.steps.map((s) => s.command), ['git status --short --branch', 'echo done'], 'a project command runs in the project');
|
|
266
|
+
x = cc.expandSteps(action([{ use: 'workspace-ddev-list' }]), ctx, env);
|
|
267
|
+
assert.strictEqual(x.entry.steps[0].command, "( cd '/w/dev' && ddev list )", 'a workspace command runs from the workspace folder');
|
|
268
|
+
x = cc.expandSteps(action([{ use: 'backup-dev-project' }]), ctx, env);
|
|
269
|
+
assert.strictEqual(x.entry.steps[0].command, "( cd '/w/dev' && bash '/w/dev/cmd-tools-backup-dev.sh' 'site' )", 'a script runs from its folder, the project in its arguments');
|
|
270
|
+
x = cc.expandSteps(action([{ use: 'backup-dev-project' }]), ctx, { ...env, exists: () => false });
|
|
271
|
+
assert.match(x.error, /does not exist/);
|
|
272
|
+
x = cc.expandSteps(action([{ use: 'gone' }]), ctx, env);
|
|
273
|
+
assert.match(x.error, /step 1 uses gone, which is not a command any more/);
|
|
274
|
+
|
|
275
|
+
const switcher = action([{ use: 'git-switch-branch' }]);
|
|
276
|
+
assert.deepStrictEqual(rl.effectiveOptions(switcher).map((o) => o.name), ['branch'], 'the options of a used command are asked for');
|
|
277
|
+
assert.match(rl.actionCommandLine(cc.expandSteps(switcher, ctx, env).entry, { branch: 'main' }), /git fetch --prune && git switch 'main'/);
|
|
278
|
+
|
|
279
|
+
const report = rl.checkDefinition('custom:\n - { id: x, label: X, button: ddev, steps: [{ use: nope }] }\n', 'replace', rl.normalizeLayout({}));
|
|
280
|
+
assert.match(report.errors.join(' '), /uses nope, which is not a command/);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('buttons hold commands: added from a button or from the command, listed where used, and kept while used', async () => {
|
|
284
|
+
shipped();
|
|
285
|
+
fs.rmSync(process.env.COMMANDS_FILE, { force: true });
|
|
286
|
+
cc.invalidateCatalog();
|
|
287
|
+
const rl = require('../../row-layout');
|
|
288
|
+
rl.resetRowLayout();
|
|
289
|
+
const handlers = { ...require('../../actions/row-actions'), ...require('../../actions/commands-center') };
|
|
290
|
+
const call = async (route, form) => {
|
|
291
|
+
const out = {};
|
|
292
|
+
await handlers[route]({ form, workspace: 'dev', pathname: route, send: (html, code = 200) => { out.html = html; out.code = code; } });
|
|
293
|
+
return out;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
let r = await call('/actions/row-custom-add', { for_button: 'git', add_use_git: 'git-log', add_label_git: '', add_steps_git: '', add_when_git: 'any' });
|
|
297
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
298
|
+
const a = rl.loadRowLayoutFresh().custom.find((c) => c.id === 'last-20-commits');
|
|
299
|
+
assert.deepStrictEqual([a.label, a.group, a.when, a.steps.map(rl.stepText)], ['Last 20 commits', 'Git', ['git'], ['use: git-log']], 'a command picked alone is the whole action');
|
|
300
|
+
r = await call('/actions/row-custom-add', { for_button: 'ddev', add_label_ddev: 'Refresh', add_steps_ddev: 'use: drush-cache-rebuild\nuse: nope', add_when_ddev: 'ddev' });
|
|
301
|
+
assert.strictEqual(r.code, 400);
|
|
302
|
+
assert.match(r.html, /uses nope, which is not a command/);
|
|
303
|
+
assert.match(rl.rowActionsEditorHtml(), /name="add_use_git"[\s\S]*?<option value="git-log">Last 20 commits<\/option>/, 'each Add an action box has the picker');
|
|
304
|
+
|
|
305
|
+
r = await call('/actions/cc-save', { cc_label: 'Say where', cc_on: 'project', cc_kind: 'run', cc_run: 'pwd', mtime: String(cc.loadCatalog().mtime) });
|
|
306
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
307
|
+
r = await call('/actions/cc-add-to-button', { id: 'say-where', button: 'more', group: 'Mine', label: '', mtime: String(rl.layoutMtime()) });
|
|
308
|
+
assert.strictEqual(r.code, 200, r.html);
|
|
309
|
+
assert.match(r.html, /Added to More → Mine/);
|
|
310
|
+
assert.match(r.html, /<strong>More<\/strong> → Mine → Say where/, 'the command page lists where it is used');
|
|
311
|
+
assert.deepStrictEqual(cc.usedBy('say-where').map((x) => [x.button, x.label]), [['more', 'Say where']]);
|
|
312
|
+
|
|
313
|
+
r = await call('/actions/cc-remove', { id: 'say-where', mtime: String(cc.loadCatalog().mtime) });
|
|
314
|
+
assert.strictEqual(r.code, 409, 'a command a button uses cannot be deleted');
|
|
315
|
+
assert.match(r.html, /Say where is on a button: More → Mine → Say where/);
|
|
316
|
+
assert.match(cc.checkDefinition('commands: []\n').errors.join(' '), /removes say-where, which is used by More → Mine → Say where/);
|
|
317
|
+
r = await call('/actions/cc-reset', { mtime: String(cc.loadCatalog().mtime) });
|
|
318
|
+
assert.strictEqual(r.code, 409);
|
|
319
|
+
|
|
320
|
+
r = await call('/actions/cc-add-to-button', { id: 'file-head', button: 'more', mtime: String(rl.layoutMtime()) });
|
|
321
|
+
assert.strictEqual(r.code, 400);
|
|
322
|
+
assert.match(r.html, /runs on a file/);
|
|
323
|
+
|
|
324
|
+
rl.resetRowLayout();
|
|
325
|
+
cc.saveYourCommands([], '');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test('Commands is its own page: out of Settings, in the rail, and the old addresses send you to it', () => {
|
|
329
|
+
const src = fs.readFileSync(path.join(APP, 'server.js'), 'utf8');
|
|
330
|
+
assert.match(src, /'\/settings\/row-actions': '\/settings\/buttons'/);
|
|
331
|
+
assert.doesNotMatch(src, /tab\('\/settings\/(commands|row-actions)'/, 'Settings no longer carries the commands');
|
|
332
|
+
assert.match(src, /navLink\('\/commands', 'tabler:terminal-2', 'Commands'/);
|
|
333
|
+
assert.match(src, /\.\.\.require\('\.\/actions\/commands-center'\)/);
|
|
334
|
+
for (const route of ['/commands/c/', '/commands/s/', '/fragments/cc/run/', '/fragments/cc/script-run/']) {
|
|
335
|
+
assert.ok(src.includes(route.replace(/\//g, '\\/')), `no route for ${route}`);
|
|
336
|
+
}
|
|
337
|
+
assert.ok(!/function commandRowsHtml/.test(src), 'the old list is gone, not left beside the new one');
|
|
338
|
+
});
|