@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,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The NODE menu on a project row.
|
|
3
|
+
*
|
|
4
|
+
* What must never drift: it appears only where there is a package.json; it offers yarn and npm as tabs
|
|
5
|
+
* that say whether each lock file is there, opening on the manager the project uses; Yarn 1 and Yarn 2+
|
|
6
|
+
* get their own command forms, read from the folder; every command runs in a screen session through
|
|
7
|
+
* DDEV, never a host npm or yarn; the handler runs only its tables, a real package name and a script
|
|
8
|
+
* package.json declares; and NODE sits right after COMPOSER.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const assert = require('node:assert');
|
|
12
|
+
const { test } = require('node:test');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const node = require('../../actions/node');
|
|
18
|
+
const { MANAGERS, NODE_GROUPS, NODE_RUN_ACTIONS, NODE_PACKAGE_ACTIONS, NODE_SCRIPT_ROUTE, hasPackageJson, nodeInfo,
|
|
19
|
+
nodeArgsFor, nodePackageRefusal, nodeScripts, nodeScreenCommand } = node;
|
|
20
|
+
const { LINGER_MARK } = require('../../linger');
|
|
21
|
+
|
|
22
|
+
function folder(files) {
|
|
23
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'nodemenu-'));
|
|
24
|
+
for (const [name, body] of Object.entries(files)) {
|
|
25
|
+
fs.mkdirSync(path.dirname(path.join(dir, name)), { recursive: true });
|
|
26
|
+
fs.writeFileSync(path.join(dir, name), typeof body === 'string' ? body : JSON.stringify(body));
|
|
27
|
+
}
|
|
28
|
+
return dir;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test('every entry has a group, a handler and shell-safe forms for npm, Yarn 1 and Yarn 2+', () => {
|
|
32
|
+
assert.deepStrictEqual(MANAGERS, ['yarn', 'npm']);
|
|
33
|
+
const order = Object.values(NODE_RUN_ACTIONS).map((a) => NODE_GROUPS.indexOf(a.group));
|
|
34
|
+
assert.ok(order.every((g) => g >= 0));
|
|
35
|
+
assert.deepStrictEqual(order, [...order].sort((x, y) => x - y), 'listed group by group, Daily first');
|
|
36
|
+
for (const [route, act] of [...Object.entries(NODE_RUN_ACTIONS), ...Object.entries(NODE_PACKAGE_ACTIONS)]) {
|
|
37
|
+
assert.match(route, /^\/actions\/node-[a-z-]+$/);
|
|
38
|
+
assert.strictEqual(typeof node[route], 'function', `${route} has no handler exported`);
|
|
39
|
+
for (const form of ['npm', 'yarn1', 'yarn']) {
|
|
40
|
+
if (act[form] === null) continue;
|
|
41
|
+
assert.ok(Array.isArray(act[form]) && act[form].length, `${route} ${form}`);
|
|
42
|
+
// The manager and these args become the session's closing echo.
|
|
43
|
+
assert.match(act[form].join(' '), /^[\w .=-]+$/, `${route} ${form} is not shell-safe`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
assert.strictEqual(typeof node[NODE_SCRIPT_ROUTE], 'function');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('the folder decides the manager and which Yarn; the lock files are reported', () => {
|
|
50
|
+
const dirs = [];
|
|
51
|
+
try {
|
|
52
|
+
const yarn1 = folder({ 'package.json': { name: 'a' }, 'yarn.lock': '' }); dirs.push(yarn1);
|
|
53
|
+
let i = nodeInfo(yarn1);
|
|
54
|
+
assert.deepStrictEqual([i.valid, i.preferred, i.yarnBerry, i.locks.yarn, i.locks.npm], [true, 'yarn', false, true, false]);
|
|
55
|
+
assert.deepStrictEqual(nodeArgsFor(NODE_RUN_ACTIONS['/actions/node-ci'], 'yarn', i), ['install', '--frozen-lockfile']);
|
|
56
|
+
|
|
57
|
+
const berry = folder({ 'package.json': { packageManager: 'yarn@4.18.0+sha512.abc' }, 'yarn.lock': '' }); dirs.push(berry);
|
|
58
|
+
i = nodeInfo(berry);
|
|
59
|
+
assert.deepStrictEqual([i.preferred, i.yarnBerry, i.pinned.spec], ['yarn', true, 'yarn@4.18.0']);
|
|
60
|
+
assert.deepStrictEqual(nodeArgsFor(NODE_RUN_ACTIONS['/actions/node-ci'], 'yarn', i), ['install', '--immutable']);
|
|
61
|
+
assert.deepStrictEqual(nodeArgsFor(NODE_RUN_ACTIONS['/actions/node-outdated'], 'yarn', i), ['upgrade-interactive']);
|
|
62
|
+
assert.strictEqual(nodeArgsFor(NODE_RUN_ACTIONS['/actions/node-update'], 'yarn', i), null, 'Yarn 2+ has no bare upgrade');
|
|
63
|
+
|
|
64
|
+
const rc = folder({ 'package.json': {}, '.yarnrc.yml': 'yarnPath: x\n' }); dirs.push(rc);
|
|
65
|
+
assert.strictEqual(nodeInfo(rc).yarnBerry, true, '.yarnrc.yml alone means Yarn 2+');
|
|
66
|
+
|
|
67
|
+
const npm = folder({ 'package.json': {}, 'package-lock.json': '{}' }); dirs.push(npm);
|
|
68
|
+
i = nodeInfo(npm);
|
|
69
|
+
assert.deepStrictEqual([i.preferred, i.locks.npm, i.locks.yarn], ['npm', true, false]);
|
|
70
|
+
assert.deepStrictEqual(nodeArgsFor(NODE_RUN_ACTIONS['/actions/node-ci'], 'npm', i), ['ci']);
|
|
71
|
+
|
|
72
|
+
// The pin wins over a stray lock file.
|
|
73
|
+
const pinned = folder({ 'package.json': { packageManager: 'yarn@4.1.0' }, 'package-lock.json': '{}' }); dirs.push(pinned);
|
|
74
|
+
assert.strictEqual(nodeInfo(pinned).preferred, 'yarn');
|
|
75
|
+
|
|
76
|
+
const bad = folder({ 'package.json': '{ not json' }); dirs.push(bad);
|
|
77
|
+
assert.strictEqual(nodeInfo(bad).valid, false);
|
|
78
|
+
assert.strictEqual(hasPackageJson(bad), true);
|
|
79
|
+
assert.strictEqual(hasPackageJson(path.join(bad, 'missing')), false);
|
|
80
|
+
} finally { for (const d of dirs) fs.rmSync(d, { recursive: true, force: true }); }
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('a package must be an npm package name; only add takes a version', () => {
|
|
84
|
+
const add = NODE_PACKAGE_ACTIONS['/actions/node-add'];
|
|
85
|
+
const rm = NODE_PACKAGE_ACTIONS['/actions/node-remove'];
|
|
86
|
+
for (const ok of ['react', 'lodash.merge', '@storybook/react', '@storybook/react@^8.0', 'lodash@latest', 'sass@>=1.70 <2']) {
|
|
87
|
+
assert.strictEqual(nodePackageRefusal(ok, add), '', `should pass: ${ok}`);
|
|
88
|
+
}
|
|
89
|
+
assert.strictEqual(nodePackageRefusal('@storybook/react', rm), '');
|
|
90
|
+
assert.notStrictEqual(nodePackageRefusal('react@^18', rm), '', 'remove takes no version');
|
|
91
|
+
for (const bad of ['', 'React', "react'; id; '", 'react@$(id)', 'react@`id`', 'react;id', 'github:user/repo',
|
|
92
|
+
'file:../x', 'https://x.test/a.tgz', 'react@git+ssh://x', 'react@1\\2', '../etc']) {
|
|
93
|
+
assert.notStrictEqual(nodePackageRefusal(bad, add), '', `must be refused: ${bad}`);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('scripts come from package.json; lifecycle hooks and unsafe names do not', () => {
|
|
98
|
+
const found = nodeScripts({ scripts: {
|
|
99
|
+
'storybook:dev': 'storybook dev -p 6006',
|
|
100
|
+
build: 'webpack', prebuild: 'rimraf dist', postbuild: 'echo done',
|
|
101
|
+
prettier: 'prettier --write .', postcss: 'postcss src',
|
|
102
|
+
postinstall: 'patch-package', prepare: 'husky',
|
|
103
|
+
'bad name; id': 'x',
|
|
104
|
+
} });
|
|
105
|
+
assert.deepStrictEqual(found.map((s) => s.name), ['storybook:dev', 'build', 'prettier', 'postcss']);
|
|
106
|
+
assert.strictEqual(found[0].command, 'storybook dev -p 6006');
|
|
107
|
+
assert.deepStrictEqual(nodeScripts(null), []);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('a command runs through DDEV, never a host npm or yarn, and stays open to be read', () => {
|
|
111
|
+
const ddev = nodeScreenCommand('yarn install', 'yarn', ['install'], { isDdev: true, projectDir: '/home/u/workspace/dev/site1' });
|
|
112
|
+
assert.ok(ddev.startsWith('ddev yarn install;'), ddev);
|
|
113
|
+
const npm = nodeScreenCommand('npm ci', 'npm', ['ci'], { isDdev: false, projectDir: '/home/u/workspace/products/lib' });
|
|
114
|
+
assert.match(npm, /^cd '[^']+\/core\/workspace-app' && ddev exec -d '\/home\/u\/workspace\/products\/lib' npm ci;/, npm);
|
|
115
|
+
const pinned = nodeScreenCommand('yarn install', 'yarn', ['install'],
|
|
116
|
+
{ isDdev: false, projectDir: '/home/u/workspace/products/theme', pinned: { name: 'yarn', major: 4, spec: 'yarn@4.18.0' } });
|
|
117
|
+
assert.match(pinned, /ddev exec -d '\/home\/u\/workspace\/products\/theme' env COREPACK_ENABLE_DOWNLOAD_PROMPT=0 corepack yarn install;/, pinned);
|
|
118
|
+
for (const line of [ddev, npm, pinned]) {
|
|
119
|
+
assert.ok(!/(^|[;&|]\s*)(npm|yarn)\b/.test(line), `a bare host npm or yarn: ${line}`);
|
|
120
|
+
assert.ok(line.includes(LINGER_MARK), 'the session must wait to be read');
|
|
121
|
+
assert.ok(!line.includes('$'), `a $ would be expanded by the runner's outer shell: ${line}`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('the handler refuses anything outside its tables and bad input, before touching the folder', async () => {
|
|
126
|
+
const call = async (pathname, form) => {
|
|
127
|
+
const out = {};
|
|
128
|
+
await node[NODE_SCRIPT_ROUTE]({ form, send: (html, code = 200) => { out.html = html; out.code = code; }, workspace: 'test', pathname });
|
|
129
|
+
return out;
|
|
130
|
+
};
|
|
131
|
+
let r = await call('/actions/node-exec', { projectName: 'anything', manager: 'npm' });
|
|
132
|
+
assert.strictEqual(r.code, 404);
|
|
133
|
+
r = await call('/actions/node-install', { projectName: '../etc', manager: 'npm' });
|
|
134
|
+
assert.strictEqual(r.code, 400);
|
|
135
|
+
r = await call('/actions/node-install', { projectName: 'anything', manager: 'pnpm; id' });
|
|
136
|
+
assert.strictEqual(r.code, 400);
|
|
137
|
+
assert.match(r.html, /Pick npm or yarn/);
|
|
138
|
+
r = await call('/actions/node-add', { projectName: 'anything', manager: 'yarn', package: 'github:evil/repo' });
|
|
139
|
+
assert.strictEqual(r.code, 400);
|
|
140
|
+
assert.match(r.html, /not a package name/);
|
|
141
|
+
r = await call(NODE_SCRIPT_ROUTE, { projectName: 'anything', manager: 'yarn', script: 'x; id' });
|
|
142
|
+
assert.strictEqual(r.code, 400);
|
|
143
|
+
r = await call('/actions/node-install', { projectName: 'no-such-project-for-node-menu-test', manager: 'yarn' });
|
|
144
|
+
assert.strictEqual(r.code, 400);
|
|
145
|
+
assert.match(r.html, /has no package\.json/);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('the row shows NODE right after COMPOSER, only where there is a package.json, with a tab per manager', () => {
|
|
149
|
+
const src = fs.readFileSync(path.join(__dirname, '..', '..', 'server.js'), 'utf8');
|
|
150
|
+
assert.match(src, /\.\.\.require\('\.\/actions\/node'\)/, 'the node routes are not merged');
|
|
151
|
+
// Where the buttons sit is Commands → Buttons now (row-layout.js); by default NODE comes right after COMPOSER.
|
|
152
|
+
const { DEFAULT_LINES } = require('../../row-layout');
|
|
153
|
+
assert.strictEqual(DEFAULT_LINES[1][DEFAULT_LINES[1].indexOf('composer') + 1], 'node', 'NODE must come right after COMPOSER by default');
|
|
154
|
+
assert.strictEqual((src.match(/\bnode: nodeMenu\b/g) || []).length, 1, 'handed to the layout once per row');
|
|
155
|
+
const fn = src.slice(src.indexOf('function nodeMenuHtml'), src.indexOf('function openWithMenuHtml'));
|
|
156
|
+
assert.match(fn, /if \(!hasPackageJson\(projectPath\)\) return '';/, 'no package.json, no button');
|
|
157
|
+
assert.match(fn, /uk-tab="active: \$\{Math\.max\(0, MANAGERS\.indexOf\(info\.preferred\)\)\}"/, 'opens on the manager the project uses');
|
|
158
|
+
assert.match(fn, /info\.locks\[m\] \? 'lock ✓' : 'no lock'/, 'each tab says whether its lock file is there');
|
|
159
|
+
assert.match(fn, /hx-include="closest \.node-package"/);
|
|
160
|
+
assert.match(fn, /NODE_SCRIPT_ROUTE/, 'the scripts from package.json must be offered');
|
|
161
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The note on a folder, and the name you would rather call it.
|
|
3
|
+
*
|
|
4
|
+
* The note lives IN the folder (`.workspace-note`), which is the choice `.jira-project` already made
|
|
5
|
+
* here — so a project renamed, moved or cloned takes its note with it, one removed takes its note away,
|
|
6
|
+
* and a `claude` session started there can read it. A central index would have to be told about every
|
|
7
|
+
* one of those events, and would be wrong the first time somebody moved a folder in a terminal.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { test } = 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 notes = require('../../notes');
|
|
17
|
+
const { folderOf } = require('../../actions/notes');
|
|
18
|
+
|
|
19
|
+
const server = fs.readFileSync(path.join(__dirname, '../../server.js'), 'utf8');
|
|
20
|
+
const tmp = () => fs.mkdtempSync(path.join(os.tmpdir(), 'ws-note-'));
|
|
21
|
+
|
|
22
|
+
test('a note is one file with two fields, written as a titled markdown note', () => {
|
|
23
|
+
const dir = tmp();
|
|
24
|
+
notes.writeNote(dir, { title: 'Gleap widget spike', body: 'Reproducing WEBSHIP-1925.' });
|
|
25
|
+
// A document, not a config format: an agent that opens it gets something to read.
|
|
26
|
+
assert.equal(
|
|
27
|
+
fs.readFileSync(path.join(dir, notes.NOTE_FILE), 'utf8'),
|
|
28
|
+
'# Gleap widget spike\n\nReproducing WEBSHIP-1925.\n',
|
|
29
|
+
);
|
|
30
|
+
assert.deepEqual(notes.readNote(dir), { title: 'Gleap widget spike', body: 'Reproducing WEBSHIP-1925.' });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('either half can stand alone', () => {
|
|
34
|
+
const dir = tmp();
|
|
35
|
+
notes.writeNote(dir, { title: '', body: 'no heading, all note' });
|
|
36
|
+
assert.deepEqual(notes.readNote(dir), { title: '', body: 'no heading, all note' });
|
|
37
|
+
notes.writeNote(dir, { title: 'name only', body: '' });
|
|
38
|
+
assert.deepEqual(notes.readNote(dir), { title: 'name only', body: '' });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('emptying both fields removes the file', () => {
|
|
42
|
+
// A note nobody wants should not need a second control to get rid of, and a file holding only
|
|
43
|
+
// whitespace would keep the row marked as annotated.
|
|
44
|
+
const dir = tmp();
|
|
45
|
+
notes.writeNote(dir, { title: 'x', body: 'y' });
|
|
46
|
+
notes.writeNote(dir, { title: ' ', body: '\n' });
|
|
47
|
+
assert.equal(fs.existsSync(path.join(dir, notes.NOTE_FILE)), false);
|
|
48
|
+
assert.equal(notes.hasNote(dir), false);
|
|
49
|
+
assert.deepEqual(notes.readNote(dir), { title: '', body: '' });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('a title is one line, whatever was pasted into the field', () => {
|
|
53
|
+
const dir = tmp();
|
|
54
|
+
notes.writeNote(dir, { title: '# Hash and\nnewlines ', body: '' });
|
|
55
|
+
assert.equal(notes.readNote(dir).title, 'Hash and newlines');
|
|
56
|
+
// And it cannot push a project row sideways.
|
|
57
|
+
notes.writeNote(dir, { title: 'x'.repeat(200), body: '' });
|
|
58
|
+
assert.equal(notes.readNote(dir).title.length, notes.TITLE_MAX);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('control characters never reach a title attribute', () => {
|
|
62
|
+
// The note is rendered into `title=` and read out by a screen reader; an escape sequence pasted from
|
|
63
|
+
// a terminal line would be neither. Newlines survive, because a note is prose.
|
|
64
|
+
const dir = tmp();
|
|
65
|
+
notes.writeNote(dir, { title: '', body: 'onetwo[31m\nthree' });
|
|
66
|
+
assert.equal(notes.readNote(dir).body, 'onetwo[31m\nthree');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('displayName falls back to the folder name', () => {
|
|
70
|
+
const dir = tmp();
|
|
71
|
+
assert.equal(notes.displayName(dir, 'v11x00b1gleap'), 'v11x00b1gleap');
|
|
72
|
+
notes.writeNote(dir, { title: 'Gleap widget spike', body: '' });
|
|
73
|
+
assert.equal(notes.displayName(dir, 'v11x00b1gleap'), 'Gleap widget spike');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('a folder is only ever one inside the workspace it names', () => {
|
|
77
|
+
// The workspace and the project come from the browser; a path built from them unchecked would write
|
|
78
|
+
// .workspace-note anywhere `..` can reach.
|
|
79
|
+
assert.equal(folderOf({ workspace: 'no-such-workspace', projectName: 'x' }), null);
|
|
80
|
+
assert.equal(folderOf({ workspace: 'test', projectName: '../../etc' }), null);
|
|
81
|
+
assert.equal(folderOf({ workspace: 'test', projectName: 'definitely-not-here-1234' }), null);
|
|
82
|
+
// And a workspace with no project is the workspace folder itself, which does exist.
|
|
83
|
+
assert.ok(folderOf({ workspace: 'test' }));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('the tooltip is cut to what a tooltip can carry', () => {
|
|
87
|
+
const tip = notes.noteTooltip({ title: 'T', body: 'word '.repeat(200) });
|
|
88
|
+
assert.ok(tip.length <= 301, 'trimmed');
|
|
89
|
+
assert.match(tip, /…$/, 'and marked, so it reads as cut rather than as all there is');
|
|
90
|
+
assert.equal(notes.noteTooltip({ title: 'short', body: '' }), 'short');
|
|
91
|
+
assert.equal(notes.noteTooltip({ title: '', body: '' }), '');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('the row shows the note, and keeps the folder name beside a custom one', () => {
|
|
95
|
+
// The folder's own name is what `cd` takes, what DDEV calls the project and what every command on
|
|
96
|
+
// the row needs — so a custom name is shown WITH it, never instead of it.
|
|
97
|
+
assert.match(server, /const rawLabel = note\.title \|\| p;/);
|
|
98
|
+
assert.match(server, /class="pcard-chip pcard-slug"/);
|
|
99
|
+
assert.match(server, /title="\$\{esc\(tip \|\| rawLabel\)\}"/);
|
|
100
|
+
assert.match(server, /const sayFolder = note\.title && !note\.title\.toLowerCase\(\)\.includes\(p\.toLowerCase\(\)\);/,
|
|
101
|
+
'the folder is said beside a custom name only when the name does not already hold it');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('the pen that edits it is in the card corner, not on the name line', () => {
|
|
105
|
+
// The name is the one thing the eye scans a list of thirty cards for; a control sitting in the
|
|
106
|
+
// middle of it reads as part of the name. The pen belongs in the card's bottom-right cluster.
|
|
107
|
+
const card = server.slice(server.indexOf('<div class="uk-card uk-card-default uk-card-small uk-card-body uk-margin-small project-row">'));
|
|
108
|
+
const titleLine = card.slice(card.indexOf('project-title-row'), card.indexOf('project-title-status'));
|
|
109
|
+
assert.ok(!titleLine.includes('project-note'), 'no pen on the name line');
|
|
110
|
+
assert.match(server, /const noteCorner = `<span class="project-note-corner">/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('the cluster rides in the last row of controls, and reserves nothing', () => {
|
|
114
|
+
// It used to float in the corner, which had to be paid for with reserved padding on EVERY action
|
|
115
|
+
// row — and that padding pushed `More` onto a line of its own while the row still had room for it.
|
|
116
|
+
// As a flex item pushed right it costs nothing until the row is genuinely full.
|
|
117
|
+
assert.match(server, /\$\{testBadge\}\$\{runningBadge\}\s*\$\{noteCorner\}/, 'the cluster ends the facts row of the card');
|
|
118
|
+
const css = fs.readFileSync(path.join(__dirname, '../../public/css/components/project-rows.css'), 'utf8');
|
|
119
|
+
assert.match(css, /\.project-note-corner \{[^}]*margin-left: auto/);
|
|
120
|
+
assert.ok(!/\.project-row \.project-actions \{[^}]*padding-right/.test(css), 'no reserved gutter');
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('the pen out-specifies UIKit, which is linked after us', () => {
|
|
124
|
+
// A bare .project-note ties with UIKit's own .uk-button-link, and UIKit wins the tie — silently.
|
|
125
|
+
const css = fs.readFileSync(path.join(__dirname, '../../public/css/components/project-rows.css'), 'utf8');
|
|
126
|
+
assert.match(css, /\.project-note-corner \.project-note \{/);
|
|
127
|
+
assert.ok(!/^\.project-note \{/m.test(css), 'not a single class of ours against UIKit');
|
|
128
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
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 read = (...p) => fs.readFileSync(path.join(__dirname, '..', '..', ...p), 'utf8');
|
|
7
|
+
const server = read('server.js');
|
|
8
|
+
const screens = read('screens.js');
|
|
9
|
+
const lists = read('lists.js');
|
|
10
|
+
|
|
11
|
+
test('Home has one h1, and each workspace card title is an h2', () => {
|
|
12
|
+
assert.match(server, /<h1 class="uk-h4 uk-margin-remove">Workspaces <span class="uk-text-meta">\$\{keys\.length\}<\/span><\/h1>/);
|
|
13
|
+
assert.match(server, /<h2 class="uk-card-title uk-margin-remove uk-text-bold">\$\{esc\(w\.label\)\}<\/h2>/);
|
|
14
|
+
assert.doesNotMatch(server, /<h4 class="uk-card-title/);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('a project list is an h2 under the workspace h1', () => {
|
|
18
|
+
assert.match(server, /<h2 class="uk-h3 uk-margin-remove">\$\{esc\(heading\)\} <span class="uk-badge">/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('Playwright browsers sit in a one-column group with its own h2', () => {
|
|
22
|
+
assert.match(screens, /<h2 class="uk-h4[^"]*" id="screens-browsers-title">Playwright browsers<\/h2>\s*<div class="screens-list" data-cols="1" role="group" aria-labelledby="screens-browsers-title">/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('filters say what they filter, and the visible word is inside the accessible name', () => {
|
|
26
|
+
assert.match(server, /<label class="uk-text-meta">State\s*<select[^>]*aria-label="Filter [^"]*by state"/);
|
|
27
|
+
assert.match(server, /<label class="uk-text-meta">Type\s*<select[^>]*aria-label="File type"/);
|
|
28
|
+
assert.match(lists, /<label class="list-pager-size uk-text-meta">Per page\s*<select[^>]*aria-label="How many rows per page"/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('the Build button is as tall as the fields beside it', () => {
|
|
32
|
+
const css = read('public', 'css', 'components', 'mic-on-any-field-that-wants-typing.css');
|
|
33
|
+
assert.match(css, /\.build-form \.uk-button-primary \{[^}]*height:2\.625rem; line-height:2\.625rem;/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('swap listeners use htmx 4 event names, which are the only ones it fires', () => {
|
|
37
|
+
for (const f of ['ui.js', 'mic.js']) {
|
|
38
|
+
assert.doesNotMatch(read('public', f), /'htmx:(afterSwap|beforeSwap|afterSettle)'/, `${f} listens for an event htmx 4 never fires`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How much of the window a page's content may use.
|
|
3
|
+
*
|
|
4
|
+
* `uk-container-small` capped every page at 900px, which was the right answer while the assistant was
|
|
5
|
+
* a fixed column and the rail was another. Neither is fixed any more, so the cap is a choice — and the
|
|
6
|
+
* two things that can break quietly are the same two as with the assistant's views: it must be applied
|
|
7
|
+
* before first paint (this is the width of the main column; changing it afterwards moves the page), and
|
|
8
|
+
* the setting must be a default rather than an override.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const assert = require('node:assert');
|
|
12
|
+
const { test } = require('node:test');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const app = path.join(__dirname, '..', '..');
|
|
17
|
+
const server = fs.readFileSync(path.join(app, 'server.js'), 'utf8');
|
|
18
|
+
const ui = fs.readFileSync(path.join(app, 'public', 'ui.js'), 'utf8');
|
|
19
|
+
const css = fs.readFileSync(path.join(app, 'public', 'css', 'components', 'page-width.css'), 'utf8');
|
|
20
|
+
const { styleSettings } = require(path.join(app, 'workspaces'));
|
|
21
|
+
|
|
22
|
+
test('the width is a setting, and a typo falls back rather than rendering nothing', () => {
|
|
23
|
+
assert.ok(['comfortable', 'wide', 'full'].includes(styleSettings().pageWidth));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('it is applied before first paint, and a stored choice wins over the setting', () => {
|
|
27
|
+
const head = server.slice(server.indexOf("var d=document.documentElement"), server.indexOf('</script>'));
|
|
28
|
+
// Read through wsPref: the choice is a cookie on the hub domain, so the page is the same width on
|
|
29
|
+
// every workspace subdomain rather than one width per origin.
|
|
30
|
+
assert.match(head, /wsPref\('ws-page-width',null\)\|\|'\$\{style\.pageWidth\}'/,
|
|
31
|
+
'the setting is the default; the stored choice wins, as with style.mode');
|
|
32
|
+
assert.match(head, /indexOf\(w\)<0\)w='comfortable'/, 'an unknown width must fall back');
|
|
33
|
+
assert.match(head, /d\.dataset\.pageWidth=w/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('the control sits with the other layout controls, not in the rail', () => {
|
|
37
|
+
// How wide the page may be and how many fit across it are one question; answering it from the rail
|
|
38
|
+
// meant looking away from the thing being changed.
|
|
39
|
+
const fn = server.slice(server.indexOf('function viewSwitcherHtml'), server.indexOf('/* ---------------- pages ---------------- */'));
|
|
40
|
+
assert.match(fn, /class="view-switcher width-switcher"/);
|
|
41
|
+
for (const w of ['comfortable', 'wide', 'full']) {
|
|
42
|
+
assert.match(fn, new RegExp(`\\['${w}',`), `no way to choose ${w}`);
|
|
43
|
+
}
|
|
44
|
+
assert.ok(!/actions-width/.test(server), 'the rail row is gone');
|
|
45
|
+
assert.match(ui, /const pick = e\.target\.closest\('\.page-width-pick'\)/);
|
|
46
|
+
assert.match(ui, /wsPrefSet\('ws-page-width', width\)/, 'and it is remembered for the whole dashboard');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('the cap is beaten on specificity, never with !important', () => {
|
|
50
|
+
// UIKit's own .uk-container-small is a single class in a stylesheet linked after this one, so a
|
|
51
|
+
// single class of ours would lose the tie — the same trap the assistant's New chat button hit.
|
|
52
|
+
// .page-body, not .page-body.uk-container-small: the home page is a `large` container, and the
|
|
53
|
+
// control did nothing at all on the page most worth widening.
|
|
54
|
+
assert.match(css, /html\[data-page-width="wide"\] \.page-body \{ max-width/);
|
|
55
|
+
assert.match(css, /html\[data-page-width="full"\] \.page-body \{ max-width: none; \}/);
|
|
56
|
+
// The comment above those rules says the word; what matters is that no DECLARATION carries it.
|
|
57
|
+
const declarations = css.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
58
|
+
assert.ok(!/!important/.test(declarations), 'never !important, and never a fork of the vendored CSS');
|
|
59
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--php-version` on every builder that runs `ddev config`, as a list rather than a free-text box.
|
|
3
|
+
*
|
|
4
|
+
* The versions are the seven most recent DDEV supports, and DDEV is the authority on that: asked of it
|
|
5
|
+
* rather than remembered, it answers `[5.6 7.0 7.1 7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.4 8.5]`. A version
|
|
6
|
+
* outside the list is refused by argparse BY NAME, which is why offering it as a list matters — a
|
|
7
|
+
* typo in a text box becomes a build that dies on its first line.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { test } = require('node:test');
|
|
11
|
+
const assert = require('node:assert');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const { ROOT } = require('../../workspaces');
|
|
16
|
+
const { parseArgparseText, parseBuilderArgs } = require('../../builder-args');
|
|
17
|
+
|
|
18
|
+
const VERSIONS = ['8.5', '8.4', '8.3', '8.2', '8.1', '8.0', '7.4'];
|
|
19
|
+
const ARGS = path.join(ROOT, 'core/scripts/args');
|
|
20
|
+
const read = (f) => fs.readFileSync(f, 'utf8');
|
|
21
|
+
|
|
22
|
+
// Every argparse file a site-building builder sources. Not the site-template ones: a site-template
|
|
23
|
+
// build takes a name and nothing else, so the PHP version there is the one DDEV picks.
|
|
24
|
+
const BUILDER_ARGS = [
|
|
25
|
+
'arg-drupal.sh', 'arg-drupal_cms.sh',
|
|
26
|
+
'arg-webship.sh', 'arg-webships.sh', 'arg-cucumber.sh',
|
|
27
|
+
'arg-website.sh', 'arg-webships-project.sh',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
test('every builder argparse offers the seven versions', () => {
|
|
31
|
+
for (const f of BUILDER_ARGS) {
|
|
32
|
+
const src = read(path.join(ARGS, f));
|
|
33
|
+
const opt = parseArgparseText(src).find((a) => a.flag === '--php-version');
|
|
34
|
+
assert.ok(opt, `${f} has --php-version`);
|
|
35
|
+
assert.deepEqual(opt.choices, VERSIONS, `${f} offers exactly the seven`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('the automated-testing builders take it too, instead of hardcoding one', () => {
|
|
40
|
+
const dir = path.join(ROOT, 'test');
|
|
41
|
+
if (!fs.existsSync(dir)) return; // a clone without the workspaces built
|
|
42
|
+
const builders = fs.readdirSync(dir).filter((f) => /^cmd-automated-testing-.*-project\.sh$/.test(f));
|
|
43
|
+
assert.ok(builders.length, 'there are automated-testing builders');
|
|
44
|
+
for (const f of builders) {
|
|
45
|
+
const src = read(path.join(dir, f));
|
|
46
|
+
assert.match(src, /'-p', '--php-version'/, `${f} takes the flag`);
|
|
47
|
+
// Either the script configures DDEV itself and passes the flag through, or it delegates the whole
|
|
48
|
+
// build to a build function, which reads PHP_VERSION on its own.
|
|
49
|
+
assert.ok(/--php-version=\$\{PHP_VERSION\}/.test(src) || !/ddev config/.test(src), `${f} passes what it was given`);
|
|
50
|
+
assert.doesNotMatch(src, /--php-version=8\.4\b/, `${f} no longer hardcodes a version`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('the flag wins over the version a builder declares it needs', () => {
|
|
55
|
+
// A builder says what its core NEEDS (Drupal 12: php >= 8.5.0); somebody passing --php-version is
|
|
56
|
+
// saying what they want to build on. A version the core cannot take fails in composer with a
|
|
57
|
+
// platform requirement that names it — a better answer than silently ignoring what was asked for.
|
|
58
|
+
const fn = read(path.join(ROOT, 'core/scripts/functions/fun-build-distribution.sh'));
|
|
59
|
+
assert.match(fn, /php_version="\$\{PHP_VERSION:-\$\{site_php_version:-\}\}"/);
|
|
60
|
+
assert.match(fn, /\$\{php_version:\+--php-version=\$\{php_version\}\}/);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('nothing asked for means nothing passed, and DDEV keeps its own default', () => {
|
|
64
|
+
const fn = read(path.join(ROOT, 'core/scripts/functions/fun-build-distribution.sh'));
|
|
65
|
+
// `${x:+--php-version=$x}` expands to NOTHING when x is empty — the flag is absent rather than
|
|
66
|
+
// present and blank, which ddev config would refuse.
|
|
67
|
+
assert.match(fn, /\$\{php_version:\+/);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('an argument with choices is drawn as a list, not a text box', () => {
|
|
71
|
+
const server = read(path.join(__dirname, '../../server.js'));
|
|
72
|
+
assert.match(server, /if \(a\.choices && a\.choices\.length\) \{/);
|
|
73
|
+
assert.match(server, /<select class="uk-select uk-form-small arg-value"/);
|
|
74
|
+
// An empty default keeps "leave it alone" reachable; a builder that defaults to a real version does
|
|
75
|
+
// not get an empty option, because it has an answer already.
|
|
76
|
+
assert.match(server, /— leave it to the builder —/);
|
|
77
|
+
assert.match(server, /String\(a\.def \|\| ''\) === '' \? \[/);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('the Build form offers the dropdown on a real builder', () => {
|
|
81
|
+
const dir = path.join(ROOT, 'dev');
|
|
82
|
+
if (!fs.existsSync(dir)) return;
|
|
83
|
+
for (const b of ['cmd-webship11-0-x-project.sh', 'cmd-drupal12-recommended-project.sh']) {
|
|
84
|
+
if (!fs.existsSync(path.join(dir, b))) continue;
|
|
85
|
+
const opt = parseBuilderArgs(dir, b, ROOT).find((a) => a.flag === '--php-version');
|
|
86
|
+
assert.ok(opt, `${b} exposes it to the form`);
|
|
87
|
+
assert.deepEqual(opt.choices, VERSIONS);
|
|
88
|
+
assert.equal(opt.def, '', 'and leaves the builder in charge unless asked');
|
|
89
|
+
}
|
|
90
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Auto-mode confirm card, and the poll interval beside it.
|
|
3
|
+
*
|
|
4
|
+
* Both of these broke the same way: something the browser accepts silently. htmx parses only
|
|
5
|
+
* ms/s/m, so `every 6h` was an interval of ZERO and one span re-requested itself about
|
|
6
|
+
* forty-five times a second; and deep-chat draws its messages in a SHADOW ROOT, so a plain
|
|
7
|
+
* `e.target.closest('.plan-run')` never saw the button and "Run it" did nothing at all.
|
|
8
|
+
* Neither failed loudly, which is why they are pinned here.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { test } = require('node:test');
|
|
12
|
+
const assert = require('node:assert');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const ui = fs.readFileSync(path.join(__dirname, '../../public/ui.js'), 'utf8');
|
|
17
|
+
const server = fs.readFileSync(path.join(__dirname, '../../server.js'), 'utf8');
|
|
18
|
+
const htmx = fs.readFileSync(path.join(__dirname, '../../public/htmx.min.js'), 'utf8');
|
|
19
|
+
|
|
20
|
+
test('htmx parses only ms, s and m — so no poll may be declared in hours or days', () => {
|
|
21
|
+
// Read from the vendored library rather than asserted from memory: a later htmx may add units,
|
|
22
|
+
// and this test should be what notices.
|
|
23
|
+
assert.match(htmx, /\(ms\|s\|m\)\?/,
|
|
24
|
+
'htmx parseInterval still accepts ms/s/m only');
|
|
25
|
+
const bad = [...server.matchAll(/hx-trigger="[^"]*every\s+[\d.]+\s*([a-z]+)"/g)]
|
|
26
|
+
.map((m) => m[1]).filter((u) => !['ms', 's', 'm'].includes(u));
|
|
27
|
+
assert.deepStrictEqual(bad, [],
|
|
28
|
+
'an interval htmx cannot parse is undefined, and setTimeout treats that as zero');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('the version hint polls in minutes', () => {
|
|
32
|
+
assert.match(server, /hx-trigger="load, every 360m"/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('"Run it" reaches through the shadow root and sends the turn through deep-chat', () => {
|
|
36
|
+
const block = ui.slice(ui.indexOf('// The Auto-mode confirm card'), ui.indexOf('// The image picker in settings'));
|
|
37
|
+
assert.ok(block, 'the confirm-card handler is still there');
|
|
38
|
+
assert.match(block, /composedPath\(\)/,
|
|
39
|
+
'a click inside deep-chat retargets to the host element, so closest() sees nothing');
|
|
40
|
+
assert.match(block, /submitUserMessage/,
|
|
41
|
+
'there is no .chat-input form any more — the panel owns its own input');
|
|
42
|
+
assert.doesNotMatch(block, /querySelector\('\.chat-input'\)/,
|
|
43
|
+
'the old HTMX chat form is gone; looking for it is what made the button do nothing');
|
|
44
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The catalogue: what the empty dashboard suggests and the Plugins page offers to require. Every
|
|
3
|
+
* entry is an npm package of the one scope, named once, and the page marks what is already required.
|
|
4
|
+
*/
|
|
5
|
+
const assert = require('node:assert');
|
|
6
|
+
const { test } = require('node:test');
|
|
7
|
+
|
|
8
|
+
const { FAMILIES, SCOPE, catalog } = require('../../plugin-catalog');
|
|
9
|
+
|
|
10
|
+
test('every catalogue entry is an @webship.co/workspace-<name> package, named once, in a family that says what it is for', () => {
|
|
11
|
+
assert.strictEqual(SCOPE, '@webship.co');
|
|
12
|
+
const all = catalog();
|
|
13
|
+
assert.ok(all.length >= 50, `${all.length} plugins`);
|
|
14
|
+
const names = all.map((p) => p.name);
|
|
15
|
+
assert.strictEqual(new Set(names).size, names.length, 'no plugin is listed twice');
|
|
16
|
+
for (const p of all) {
|
|
17
|
+
assert.match(p.name, /^[a-z0-9-]+$/);
|
|
18
|
+
assert.strictEqual(p.module, `${SCOPE}/workspace-${p.name}`);
|
|
19
|
+
assert.ok(p.label && p.familyLabel);
|
|
20
|
+
}
|
|
21
|
+
for (const f of FAMILIES) assert.ok(f.about && f.plugins.length, `${f.id} says what it is for and holds plugins`);
|
|
22
|
+
for (const must of ['dev', 'github', 'upsun', 'lando', 'git', 'ide', 'drupal-modules']) assert.ok(names.includes(must), `${must} is in the catalogue`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('the Plugins page marks what is already required and offers the rest', () => {
|
|
26
|
+
const { catalogHtml } = require('../../plugins-page');
|
|
27
|
+
const html = catalogHtml(new Set(['@webship.co/workspace-git']));
|
|
28
|
+
assert.match(html, /Catalogue/);
|
|
29
|
+
assert.ok((html.match(/<span class="uk-label">required<\/span>/g) || []).length === 1, 'git is marked required, once');
|
|
30
|
+
assert.match(html, /"spec":"@webship\.co\/workspace-upsun"/, 'the rest get a Require button with the package to install');
|
|
31
|
+
});
|